| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- # -*- coding: utf-8 -*-
- """Insert 8.28 screenshots back into 9.19 markdown manuals."""
- import json, re
- from pathlib import Path
- ROOT = Path(r"D:\产线\大洋\大洋操作手册汇总9.19")
- IMG = ROOT / "images"
- IMG_RE = re.compile(r"^\s*!\[.*?\]\(images/.*?\)\s*$")
- CAP_RE = re.compile(r"^\s*<p class=\"imgcap\">.*$</s>$")
- FASCICLES = [
- "1、系统管理操作手册.md",
- "2、设备管理操作手册.md",
- "3、物料管理操作手册.md",
- "4、质量管理操作手册.md",
- "5、生产管理操作手册.md",
- "6、ERP对接操作手册.md",
- "7、生产线管理操作手册.md",
- "8、PDA操作说明操作手册.md",
- "9、MES系统客户端员工扫码操作手册.md",
- ]
- def norm(s):
- s = (s or "").replace(" ", "").replace(" ", "")
- s = s.replace("→", "").replace("—", "").replace("-", "")
- s = re.sub(r"^\d+(\.\d+)*\.?", "", s)
- return s
- def parse_sections(lines):
- """Return list of {level,title,start,end} covering body after heading."""
- secs = []
- for i, line in enumerate(lines):
- m = re.match(r"^(#{2,6})\s+(.*)$", line)
- if m:
- secs.append({"level": len(m.group(1)), "title": m.group(2).strip(), "start": i})
- for i, sec in enumerate(secs):
- sec["end"] = secs[i + 1]["start"] if i + 1 < len(secs) else len(lines)
- return secs
- def strip_old_images(text):
- lines = text.splitlines()
- out = []
- i = 0
- while i < len(lines):
- line = lines[i]
- if IMG_RE.match(line) or line.startswith('<p class="imgcap">') or line.strip() == "*(界面截图取自 8.28 手册,现场如有改动以实际为准。)*":
- i += 1
- # skip following blank
- if i < len(lines) and not lines[i].strip():
- i += 1
- continue
- if line.strip() == "## 原手册界面截图(8.28)":
- # drop this section until next heading
- i += 1
- while i < len(lines) and not re.match(r"^## ", lines[i]):
- i += 1
- continue
- out.append(line)
- i += 1
- return "\n".join(out).rstrip() + "\n"
- def score(sec, cap, heading, body=""):
- title = sec["title"]
- tn, cn, hn = norm(title), norm(cap), norm(heading)
- blob = tn + norm(body)
- s = 0
- if cn and (cn in tn or tn in cn):
- s += 60
- if hn and (hn in tn or tn in hn):
- s += 45
- hay = (title + " " + body).upper()
- for code in set(re.findall(r"OP\d+", (cap + heading).upper())):
- if code and code in hay:
- s += 80
- keys = re.findall(r"[\u4e00-\u9fff]{2,}", cap + " " + heading)
- for k in keys:
- if k in title or k in body[:800]:
- s += 12
- return s
- def image_md(item):
- cap = item.get("caption") or "界面截图"
- rel = item["file"]
- if not rel.startswith("images/"):
- rel = "images/" + rel
- return "\n\n\n*(界面截图取自 8.28 手册,现场如有改动以实际为准。)*\n" % (cap, rel)
- def insert_into(md_name, items):
- path = ROOT / md_name
- text = strip_old_images(path.read_text(encoding="utf-8"))
- lines = text.splitlines()
- secs = parse_sections(lines)
- if not secs:
- path.write_text(text + "\n## 原手册界面截图(8.28)\n" + "".join(image_md(x) for x in items), encoding="utf-8")
- return len(items), 0
- buckets = {id(sec): [] for sec in secs}
- unmatched = []
- for item in items:
- best, best_s = None, 0
- for sec in secs:
- body = "\n".join(lines[sec["start"]:sec["end"]])
- sc = score(sec, item.get("caption", ""), item.get("heading", ""), body)
- if sc > best_s or (best is not None and sc == best_s and sec["level"] > best["level"]):
- best, best_s = sec, sc
- # special: login / home screens
- cap = item.get("caption") or ""
- if "登录" in cap:
- for sec in secs:
- if "登录" in sec["title"] or "安装" in sec["title"]:
- best, best_s = sec, max(best_s, 80)
- if "主功能" in cap or "我的界面" in cap:
- for sec in secs:
- if "界面" in sec["title"]:
- best, best_s = sec, max(best_s, 80)
- if best is None or best_s < 12:
- unmatched.append(item)
- else:
- buckets[id(best)].append(item)
- # insert from bottom so indexes stay valid
- for sec in reversed(secs):
- imgs = buckets[id(sec)]
- if not imgs:
- continue
- # insert before sec['end'] (start of next heading), after trailing blanks of this section
- pos = sec["end"]
- while pos > sec["start"] + 1 and pos <= len(lines) and (pos == len(lines) or not lines[pos - 1].strip()):
- pos -= 1
- block = "".join(image_md(x) for x in imgs).splitlines()
- lines[pos:pos] = [""] + block + [""]
- text = "\n".join(lines).rstrip() + "\n"
- if unmatched:
- extra = ["", "## 原手册界面截图(8.28)", "", "以下截图未能自动对上新目录,仍予保留。", ""]
- for x in unmatched:
- extra.extend(image_md(x).splitlines())
- text = text.rstrip() + "\n" + "\n".join(extra) + "\n"
- path.write_text(text, encoding="utf-8")
- return len(items) - len(unmatched), len(unmatched)
- def main():
- total_m, total_u = 0, 0
- for md in FASCICLES:
- stem = md[:-3]
- man = IMG / stem / "manifest.json"
- if not man.exists():
- print("no manifest", stem)
- continue
- items = json.loads(man.read_text(encoding="utf-8"))
- # keep only files that exist
- ok = []
- for it in items:
- p = ROOT / it["file"]
- if p.exists():
- ok.append(it)
- m, u = insert_into(md, ok)
- total_m += m
- total_u += u
- print(stem, "matched", m, "appendix", u, "total", len(ok))
- print("ALL matched", total_m, "appendix", total_u)
- if __name__ == "__main__":
- main()
|