Files
Aislo/resources/tester/test_833_1_source_notes.py
T
eomsangdonandClaude Sonnet 5.5 5e6037270e fix(833-1): 원문 패널 표 깨짐 — 표 맨 아래 비고 · [주] · ※ 서술 줄을 표 밖 글로 · 서술 칸 줄바꿈
- 새 순수 계산 logic/M01_MasterData_logic_source_notes.ts (서술 줄 가르기 · 줄바꿈 칸 판정) · 원문 글 · 차례 그대로
- 원문 표 2,573 · 폭 420 기준 서술 줄 때문에 넘친 표 138 → 0 · 넘침 1,228 → 1,020
- 시험 test_833_1_source_notes (보기 + 전수 글 보존)

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-15 10:06:06 +09:00

98 lines
3.8 KiB
Python

"""833-1 지킴 — 원문 표 맨 아래 서술 줄(비고 · [주] · ※) 가르기 · 서술 칸 줄바꿈 판정.
순수 TS(`M01_MasterData/logic/M01_MasterData_logic_source_notes.ts`)를 node 로 돌림 · 서버 · 화면 없음.
"""
from __future__ import annotations
import json
import shutil
import subprocess
import sys
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
pytestmark = pytest.mark.skipif(shutil.which("node") is None, reason="node 가 없음")
TS = (ROOT / "M01_MasterData/logic/M01_MasterData_logic_source_notes.ts").as_uri()
def _node(body: str, data: object = None) -> object:
src = f'import {{ noteLine, splitTableNotes, wrapCell }} from "{TS}";\n' + body
done = subprocess.run(
["node", "--experimental-strip-types", "--input-type=module", "-e", src],
input=json.dumps(data, ensure_ascii=False),
capture_output=True,
text=True,
encoding="utf-8",
timeout=120,
cwd=ROOT,
)
assert done.returncode == 0, done.stderr
return json.loads(done.stdout)
def test_보기_서술_줄_가르기() -> None:
got = _node(
r"""
const t = [
["구분", "단위", "수량"],
["보통인부", "인", "1"],
["비고", "- 비계주위 보호망을 시공하지 않는 경우 본 시공량의 8%를 증하여 적용한다.", ""],
["※ 위 표에서 ‘노’는 ‘노지묘’이다.", "", ""],
];
console.log(JSON.stringify([splitTableNotes(t), noteLine(["공종", "Belt폭", ""]),
noteLine(["뒷채움 자재비 및 운반비 별산", "", ""]), noteLine(["1", "2", "3"]),
noteLine(["단독"]), splitTableNotes([["비고", "긴 글이 머리 줄에 있어도 머리는 몸"]]).notes.length,
[wrapCell("7", 3), wrapCell("가".repeat(30), 3), wrapCell("글", 1), wrapCell("짧음\n" + "나".repeat(31), 2)]]));
"""
)
split = got[0]
assert split["rows"] == [["구분", "단위", "수량"], ["보통인부", "인", "1"]]
assert [n["label"] for n in split["notes"]] == ["비고", ""]
assert split["notes"][1]["text"].startswith("※ 위 표에서")
assert got[1:6] == [
None,
None,
None,
None,
0,
] # 짧은 묶음 머리 · 숫자 줄 · 한 칸 · 머리 줄은 서술 아님
assert got[6] == [False, True, True, True]
def test_원문_전수_글_그대로_표_밖으로() -> None:
"""품셈 본문 표 전부 — 줄 수 · 글 합계가 가르기 앞뒤 같음 · 머리 줄은 몸에 남음 · 표 밖 글 표는 200 넘음."""
from M01_MasterData import M01_MasterData_Store as _s # noqa: F401
from M01_MasterData import M01_MasterData_Store_Make as mk
tables = []
for book in mk.BOOKS:
for _d, _n, _t, folder in mk._chapters(book):
for md in sorted(folder.glob("*.md")):
for b in mk._blocks(md.read_text(encoding="utf-8").split("\n")):
if "table" in b:
tables.append(b["table"])
got = _node(
r"""
const tables = JSON.parse(await new Promise((ok) => { let s = ""; process.stdin.on("data", (d) => (s += d)); process.stdin.on("end", () => ok(s)); }));
let lost = 0, moved = 0, headBad = 0;
for (const t of tables) {
const { rows, notes } = splitTableNotes(t);
const had = t.flat().join("").replace(/\s/g, "");
const now = [...rows.flat(), ...notes.flatMap((n) => [n.label, n.text])].join("").replace(/\s/g, "");
if (had !== now || rows.length + notes.length !== t.length) lost++;
if (rows.length < 1) headBad++;
if (notes.length) moved++;
}
console.log(JSON.stringify({ n: tables.length, lost, moved, headBad }));
""",
tables,
)
assert got["n"] > 2500 and got["lost"] == 0 and got["headBad"] == 0
assert got["moved"] > 200