feat: 가상화 문서들 추가
This commit is contained in:
Executable
+105
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python3
|
||||
"""그림 안 <text> 가 전부 이름인지 본다.
|
||||
|
||||
`code-tables-diagrams.md` 의 자가 점검을 기계로 옮긴 것이다. 그림은 관계를 보이고
|
||||
문장은 `<desc>` 와 옆 문단에 둔다. 길이가 아니라 **서술하느냐** 가 기준이다.
|
||||
|
||||
python3 scripts/check-figure-text.py [프로젝트 ...]
|
||||
python3 scripts/check-figure-text.py --file 그림.svg
|
||||
|
||||
`<title>` 과 `<desc>` 는 화면을 못 보는 사람이 듣는 자리라 검사하지 않는다.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
TEXT = re.compile(r"<text[^>]*>([^<]*)</text>")
|
||||
|
||||
# 서술어로 끝난다 — `~있다`, `~막는다`, `~바꾼다`
|
||||
PREDICATE = re.compile(
|
||||
r"(?:이다|아니다|있다|없다|한다|된다|본다|같다|다르다|늘어난다|줄어든다"
|
||||
r"|막는다|바꾼다|만든다|넣는다|뺀다|남는다|생긴다|끝난다)$")
|
||||
|
||||
# 조사로 두 대상을 잇는다 — `A를 B로`, `A에서 B까지`
|
||||
# 관형형 어미(`깊은 페이지`)와 겹치는 는·은·이·가 는 넣지 않는다. 그쪽은 서술어 규칙이 잡는다
|
||||
JOINING = re.compile(
|
||||
r"[가-힣A-Za-z0-9_)\]]+(?:을|를|에서|에게|에는|으로|로서|로써|으로써|마다|부터|까지|보다|처럼)\s+\S")
|
||||
|
||||
# 문장 부호로 끝난다. `…` 과 축약형 `(?,…)` 은 뺀다
|
||||
SENTENCE_END = re.compile(r"[.?!]$")
|
||||
|
||||
|
||||
def offences(label: str) -> list[str]:
|
||||
t = label.strip()
|
||||
if not t:
|
||||
return []
|
||||
found = []
|
||||
if SENTENCE_END.search(t) and not t.endswith(("…", "..")):
|
||||
found.append("문장 부호로 끝난다")
|
||||
if PREDICATE.search(t):
|
||||
found.append("서술어가 있다")
|
||||
if JOINING.search(t):
|
||||
found.append("조사로 두 대상을 잇는다")
|
||||
return found
|
||||
|
||||
|
||||
def check(path: str) -> list[tuple[str, list[str]]]:
|
||||
body = open(path, encoding="utf-8").read()
|
||||
out = []
|
||||
for label in TEXT.findall(body):
|
||||
why = offences(label)
|
||||
if why:
|
||||
out.append((label.strip(), why))
|
||||
return out
|
||||
|
||||
|
||||
def svgs_of(project: str) -> list[str]:
|
||||
assets = os.path.join(ROOT, "docs", project, "final", "assets")
|
||||
return sorted(glob.glob(os.path.join(assets, "**", "*.svg"), recursive=True))
|
||||
|
||||
|
||||
def main() -> int:
|
||||
ap = argparse.ArgumentParser(description="그림 안 <text> 가 전부 이름인지 본다.")
|
||||
ap.add_argument("projects", nargs="*")
|
||||
ap.add_argument("--file", action="append", default=[],
|
||||
help="프로젝트 대신 SVG 파일을 직접 준다")
|
||||
args = ap.parse_args()
|
||||
|
||||
targets = list(args.file)
|
||||
if not targets:
|
||||
projects = args.projects or sorted(
|
||||
d for d in os.listdir(os.path.join(ROOT, "docs"))
|
||||
if not d.startswith("_")
|
||||
and os.path.isdir(os.path.join(ROOT, "docs", d, "final")))
|
||||
for project in projects:
|
||||
targets.extend(svgs_of(project))
|
||||
|
||||
total = 0
|
||||
files = 0
|
||||
for path in targets:
|
||||
hits = check(path)
|
||||
if not hits:
|
||||
continue
|
||||
files += 1
|
||||
total += len(hits)
|
||||
print(f" {os.path.relpath(path, ROOT)}")
|
||||
for label, why in hits:
|
||||
print(f" {label} ← {' · '.join(why)}")
|
||||
|
||||
checked = len(targets)
|
||||
if total:
|
||||
print(f"\nFIGURE TEXT: FAIL — 그림 {checked}장 · 문장 {total}건 / 그림 {files}장")
|
||||
print("그림 안 <text> 는 이름만 담는다. 문장은 <desc> 와 옆 문단으로 내린다.")
|
||||
return 1
|
||||
print(f"FIGURE TEXT: PASS — 그림 {checked}장 · 문장 0건")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user