165 lines
4.6 KiB
Python
165 lines
4.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""품셈 PDF → 절별 md 도구 (규칙: `원가계산/_품셈md_작성규칙.md` · 2026-09-19).
|
|
|
|
판정 기준은 PDF 뿐 — 원가계산(B09)·공종 마스터·옛 md 는 보지 않음.
|
|
|
|
사용: python pum_md_tool.py plan <산림|건설> 차례에서 장·절 → 본문/ 빈 파일(머리만 · 상태 대기)
|
|
python pum_md_tool.py pages <산림|건설> [쪽범위] 쪽 그림 150dpi PNG → tmp/pum_pages/ (git 밖)
|
|
python pum_md_tool.py check <md 파일> ① 수 대조 ② 글자 대조 → 머리 「기계검사」 칸
|
|
python pum_md_tool.py status 장별 대기/작성중/검증대기/확정 표
|
|
|
|
절 경계 = 시작표지·끝표지 글자가 쪽 안에서 놓인 줄(좌표로 묶은 줄의 맨 앞).
|
|
좌표로 줄 묶기는 `resources/tester/test_const_pdf_coverage.py` 의 것을 그대로 씀(그 시험은 안 고침).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import collections
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from pum_md_base import ( # noqa: E402,F401 — 바탕(책 설정·줄 읽기·머리 찾기)은 pum_md_base.py
|
|
_NUM,
|
|
BOOKS,
|
|
COST,
|
|
HEAD_KEYS,
|
|
ROOT,
|
|
STATES,
|
|
_page_rows,
|
|
find_heading,
|
|
heading_pattern,
|
|
join_spaced,
|
|
key,
|
|
locate,
|
|
read_rows,
|
|
visual_text,
|
|
)
|
|
|
|
from pum_md_plan import ( # noqa: E402,F401 — 차례 읽기 · 새 md 틀은 pum_md_plan.py
|
|
Entry,
|
|
_BAD_NAME,
|
|
_DIVISION,
|
|
_EMPTY_PAGE_CHARS,
|
|
_INLINE,
|
|
_LEADER,
|
|
_MAX_PAGES,
|
|
_REVISION,
|
|
_SLOT,
|
|
_TOC_ID,
|
|
_TOC_SKIP,
|
|
_blank,
|
|
_body_rows,
|
|
_is_footer,
|
|
_side_number,
|
|
_span,
|
|
_span_of,
|
|
claimed_elsewhere,
|
|
file_label,
|
|
head_text,
|
|
name_of,
|
|
parse_toc,
|
|
parse_toc_inline,
|
|
plan,
|
|
read_head,
|
|
safe,
|
|
toc_entries,
|
|
)
|
|
from pum_md_check import ( # noqa: E402,F401 — 기계검사는 pum_md_check.py
|
|
_COMMENT,
|
|
_ESCAPED,
|
|
_EXEMPT,
|
|
_GLUED,
|
|
_HOLD,
|
|
_IMAGE,
|
|
_ITEM_HEAD,
|
|
_NOT_REPEAT,
|
|
_OFF_CLOSE,
|
|
_OFF_MARK,
|
|
_OFF_OPEN,
|
|
_PUA,
|
|
_SPACED,
|
|
_SPACED_PAIR,
|
|
_TABLE_RULE,
|
|
_chars,
|
|
_numbers,
|
|
_repeat_budget,
|
|
_repeat_run,
|
|
_rewrite,
|
|
_same_han,
|
|
_split_off,
|
|
_tight,
|
|
book_of,
|
|
check,
|
|
check_file,
|
|
compare,
|
|
fingerprint,
|
|
join_spaced_numbers,
|
|
md_lines,
|
|
passed,
|
|
pdf_lines,
|
|
set_head,
|
|
split_glued_numbers,
|
|
split_head,
|
|
verdict,
|
|
)
|
|
|
|
# ── pages · status ─────────────────────────────────────────────────────
|
|
PAGES_DIR = ROOT / "tmp/pum_pages"
|
|
|
|
|
|
def pages(book_name: str, span: str = "") -> list[Path]:
|
|
"""쪽 그림 150dpi PNG — `tmp/pum_pages/<책>/p0135.png`(git 밖)."""
|
|
import pymupdf
|
|
|
|
out = PAGES_DIR / book_name
|
|
out.mkdir(parents=True, exist_ok=True)
|
|
made = []
|
|
with pymupdf.open(BOOKS[book_name]["pdf"]) as doc:
|
|
first, _, last = (span or f"1~{doc.page_count}").replace("-", "~").partition("~")
|
|
for number in range(int(first), int(last or first) + 1):
|
|
path = out / f"p{number:04d}.png"
|
|
doc[number - 1].get_pixmap(dpi=150).save(path)
|
|
made.append(path)
|
|
return made
|
|
|
|
|
|
def status() -> str:
|
|
"""장별 상태 표(md) — 머리 「상태」 칸을 모음."""
|
|
rows = [
|
|
"| 책 | 장 | " + " | ".join(STATES) + " | 계 |",
|
|
"|---|---|" + "---|" * (len(STATES) + 1),
|
|
]
|
|
for name, book in BOOKS.items():
|
|
by_chapter: dict[str, collections.Counter] = {}
|
|
for path in sorted(book["out"].rglob("*.md")):
|
|
chapter = path.parent.relative_to(book["out"]).as_posix()
|
|
by_chapter.setdefault(chapter, collections.Counter())[
|
|
read_head(path).get("상태", "")
|
|
] += 1
|
|
for chapter, counts in by_chapter.items():
|
|
cells = " | ".join(str(counts[s]) for s in STATES)
|
|
rows.append(f"| {name} | {chapter} | {cells} | {sum(counts.values())} |")
|
|
return "\n".join(rows)
|
|
|
|
|
|
def main(argv: list[str]) -> int:
|
|
command, args = (argv[0], argv[1:]) if argv else ("", [])
|
|
if command == "plan" and args:
|
|
print(f"새 파일 {len(plan(args[0]))}")
|
|
return 0
|
|
if command == "pages" and args:
|
|
made = pages(args[0], args[1] if len(args) > 1 else "")
|
|
print(f"쪽 그림 {len(made)} → {PAGES_DIR / args[0]}")
|
|
return 0
|
|
if command == "check" and args:
|
|
return max(check(Path(a)) for a in args)
|
|
if command == "status":
|
|
print(status())
|
|
return 0
|
|
print(__doc__)
|
|
return 2
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main(sys.argv[1:]))
|