- 새 순수 계산 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>
98 lines
3.8 KiB
Python
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
|