# -*- coding: utf-8 -*- """PDF → md 변환 손실 검증: 정규화 문자 커버리지 + 줄 단위 누락 확인.""" import re from pathlib import Path import os as _os from pathlib import Path as _P # 이 스크립트는 original/_pipeline/ 에 위치. 코퍼스 루트 = 상위 폴더. ROOT_DIR = _P(__file__).resolve().parent.parent # ...\original DATA_DIR = _P(__file__).resolve().parent / "data" # 생성 데이터(JSON) # API 키: 커밋 금지 파일(law/.secrets.local.md) 또는 환경변수에서 읽는다. def _load_key(name): v = _os.environ.get(name) if v: return v.strip() sec = ROOT_DIR.parent / ".secrets.local.md" if sec.exists(): import re as _re for pat in (r"KCSC[\s\S]*?`([A-Za-z0-9]{20,})`", r"인증키[:\s]*`?([A-Za-z0-9]{30,})`?"): m = _re.search(pat, sec.read_text(encoding="utf-8")) if m: return m.group(1) return "" import pymupdf ROOT = Path(str(ROOT_DIR)) def norm(s): return re.sub(r"[^가-힣0-9A-Za-z%㎞㎡㎥℃]", "", s) bad, miss_lines, empty = [], [], [] tot = 0 for pdf in sorted(list(ROOT.rglob("별표/*.pdf"))+list(ROOT.rglob("첨부/*.pdf"))): md = pdf.with_suffix(".md") if not md.exists(): bad.append((pdf.name, "md 없음", 0, 0)) continue tot += 1 d = pymupdf.open(pdf) ptxt = "\n".join(p.get_text() for p in d) pn, mn = norm(ptxt), norm(md.read_text(encoding="utf-8")) if len(pn) < 30: empty.append(pdf.name) continue # 1) 전체 문자량 비교 ratio = len(mn) / len(pn) if pn else 0 if ratio < 0.985: bad.append((str(pdf.relative_to(ROOT)), f"문자 {len(pn)}→{len(mn)}", ratio, 0)) # 2) PDF 줄 중 md에서 못 찾는 것 lost = 0 for ln in ptxt.split("\n"): k = norm(ln) if len(k) < 12: continue if k[:12] not in mn: lost += 1 if lost: miss_lines.append((str(pdf.relative_to(ROOT)), lost)) print(f"검사 {tot}건 / 본문 거의 없음(서식류) {len(empty)}건") print(f"\n문자 손실 의심 {len(bad)}건") for n, s, r, _ in sorted(bad, key=lambda x: x[2])[:15]: print(f" {r:5.3f} {s:24s} {n[-70:]}") print(f"\n줄 누락 의심 {len(miss_lines)}건") for n, c in sorted(miss_lines, key=lambda x: -x[1])[:15]: print(f" {c:4d}줄 {n[-72:]}")