Files
Aislo/resources/tester/test_642_6_screen.py
T
eomsangdonandClaude Opus 5.5 65bafe0050 feat(785-6): 785-5 실제 길 꼴로 맞춤 — 칠 = Judge.judged · 옮기기 덮음 물음 · 목록 셈 중 다시 읽음
- M02_Hopyo_Marks.py: 블록 판정 = M02_Hopyo_Judge.judged(셈 중 None = 옛 판정)
- Hopyo_Rows.ts: [옮기기] 409(이미 옮김) = 물음 한 번 뒤 덮음
- Hopyo_SheetSk.ts: 목록 셈중 = 3 초마다 다시 읽음(판정 · 고를 것 실림)
- JudgeApi · logic/Picks: 절 몫 = 수 · 목록 셈중 · 몸 덮음
- 시험: test_642_6 Marks 부름 대역 · helper_778_5 Check 대역

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-04 23:23:26 +09:00

273 lines
11 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""642-6(PLAN 785-6) 판정 화면 지킴 — 줄 판 · 블록 판 · 고름 캐시(설계 642-A 5-3 다섯째 줄).
- 부품 상자만(날 `<button>` · `<input>` 0 · 색은 공통 값) · 700 줄.
- 고름 캐시 순수 셈(`logic/Hopyo_Picks` · cjs 도우미) — 북부청 실제 줄 판(785-3 `Row.rows`) +
블록 풀이(785-2 `Solve.block`)를 그대로 넣어 봄: 제안 = 미리 체크만(빈 캐시 = 옮길 것 0) ·
[호표 제안 모두 확인] = 다 풀린 제안만 · 예시 1(단산10 GC001140 입력 필요 0) · 예시 2(단산47 기초 둘 ·
구분 엇갈림 → 토목) · 예시 3(단산24 GC000211 높이감률 · 구멍땜 입력 필요 → 0) · 옮기기 몸 = 확인한 것만 ·
막는 칸 남은 줄 막음 · `필요` 걷음 · 배 얹음.
읽기만(엑셀 · M01) · storage 안 씀.
"""
import json
import re
import subprocess
from pathlib import Path
import pytest
from M02_MasterTemplete import M02_Hopyo_Bind as hb
from M02_MasterTemplete import M02_Hopyo_Clause as hc
from M02_MasterTemplete import M02_Hopyo_Excel as hx
from M02_MasterTemplete import M02_Hopyo_Row as hr
from M02_MasterTemplete import M02_Hopyo_Solve as hs
ROOT = Path(__file__).resolve().parents[2]
HERE = Path(__file__).resolve().parent
M02 = ROOT / "M02_MasterTemplete"
NORTH = (
ROOT
/ "resources/knowledge/original/실무문서/2026_실무일위대가 모음"
/ "2026년_북부지방산림청_임도사업_설계내역서(공통단가).xls"
)
NEW = [
M02 / "M02_MasterTemplete_Hopyo_Rows.ts",
M02 / "M02_MasterTemplete_Hopyo_Block.ts",
M02 / "M02_MasterTemplete_Hopyo_JudgeApi.ts",
M02 / "logic/M02_MasterTemplete_Hopyo_Picks.ts",
]
FILE = {"layer": "personal", "owner": None}
def _run(steps: list[dict]) -> list:
proc = subprocess.run(
["node", str(HERE / "helper_642_6_picks.cjs")],
input=json.dumps({"steps": steps}, ensure_ascii=False),
capture_output=True,
text=True,
encoding="utf-8",
check=False,
)
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
@pytest.fixture(scope="module")
def judged():
"""호표 1 · 2 줄 판 + 단산 줄 풀이(예시 셋) — 785-5 `hopyo-judge/{키}/{번}` 꼴."""
book = hx.read_book(NORTH.name, NORTH.read_bytes())
master = hb.m01.cm.master()
rows = hr.rows(book, None)
blocks = {b["번"]: b for b in book["단산"]}
out = {}
for no in (1, 2):
hopyo = next(h for h in book["호표"] if h["번"] == no)
for row in rows[no]:
if row.get("번") not in (10, 24, 47):
continue
blk = blocks[row["번"]]
cands = [{"키": c["키"], "절": c.get("절")} for c in hc.basis(blk)["후보"]]
line = next(x for x in hopyo["줄"] if (x.get("참조") or {}).get("번") == row["번"])
same = [x for x in hopyo["줄"] if x is not line]
row["풀이"] = hs.block(master, blk, hs.parts(blk, cands), {"줄": line, "같은줄": same})
out[no] = rows[no]
return out
def _row(judged, no, sk):
return next(r for r in judged[no] if r.get("번") == sk)
def test_부품상자만_700줄():
for f in NEW:
text = f.read_text(encoding="utf-8")
assert len(text.splitlines()) <= 700, f.name
assert not re.search(r"""createElement\(\s*["'](button|input)["']""", text), f.name
assert not re.search(r"""\bel\(\s*["'](button|input|select)["']""", text), f.name
assert "<button" not in text and "<input" not in text, f.name
css = (M02 / "M02_MasterTemplete_Hopyo_Judge.css").read_text(encoding="utf-8")
assert not re.search(r"#[0-9a-fA-F]{3,8}\b|rgba?\(", css)
picks = NEW[3].read_text(encoding="utf-8")
assert not re.search(r"sessionStorage|fetch\(|document\.", picks.split("*/", 1)[1])
def test_제안은_미리체크만_자동확인없음(judged):
rows = judged[1]
got = _run(
[
{"fn": "emptyDraft", "args": []},
{"fn": "moveBody", "args": [{"$": 0}, FILE]},
*[{"fn": "rowDefaults", "args": [r]} for r in rows],
]
)
assert got[1]["수"] == 0 and got[1]["body"]["호표"] == {}
defaults = dict(zip([r["차례"] for r in rows], got[2:]))
first = rows[0]
assert first["판정"] == "제안 있음" and defaults[0][0]["키"] == first["후보"][0]["키"]
assert defaults[4][0]["갈래"] == "고정" # 후보 없음 = 고정 금액 제안
assert [p["키"] for p in defaults[5]] == ["GC000206", "GC000207"] # 예시 2 절마다 기초
def test_호표_제안_모두_확인은_다풀린_제안만(judged):
rows = judged[1]
got = _run(
[
{"fn": "emptyDraft", "args": []},
{"fn": "confirmSuggested", "args": [{"$": 0}, 1, rows]},
{"fn": "moveBody", "args": [{"$": 1}, FILE]},
]
)
on = {int(k) for k, v in got[1]["호표"]["1"].items() if v["확인"]}
assert on == {
0
} # 레미콘 별도계상 — 단산24 · 47 = 입력 필요 · 엇갈림 · 46 · 33 = 풀이 없음 · 면목 = 후보 없음
assert got[2]["수"] == 1 and list(got[2]["body"]["호표"]["1"]) == ["0"]
def test_예시1_단산10_입력필요0(judged):
row = _row(judged, 2, 10)
got = _run(
[
{"fn": "rowDefaults", "args": [row]},
{"fn": "suggestReady", "args": [row]},
{"fn": "confirmable", "args": [{"$": 0}]},
]
)
(p,) = got[0]
assert p["키"] == "GC001140" and p["필요"] == []
assert p["입력값"] == {
"운반거리": 6,
"경사각도": 35,
"운반회수": 42,
"운반무게": 575,
"회당운반량": 25,
}
assert {k: v["길"] for k, v in p["출처"].items()}["운반무게"] == "B4"
assert got[1] is True and got[2] is True
money = row["풀이"]["절"][0]["후보"][0]["금액"]["계"]
assert int(money) == 94227
def test_예시2_단산47_기초둘_구분엇갈림(judged):
row = _row(judged, 1, 47)
got = _run(
[
{"fn": "rowDefaults", "args": [row]},
{"fn": "blockedOf", "args": [{"$": 0}]},
]
)
assert [p["키"] for p in got[0]] == ["GC000206", "GC000207"]
assert got[1] == ["구분"]
fixed = _run([{"fn": "setInput", "args": [got[0][1], "구분", "토목", "B5"]}])[0]
assert fixed["입력값"]["구분"] == "토목" and fixed["출처"]["구분"] == {"길": "B5"}
assert _run([{"fn": "confirmable", "args": [[got[0][0], fixed]]}])[0] is True
secs = [s["제목"] for s in row["풀이"]["절"]]
assert len(secs) == 2 # 맡은 절 둘
def test_예시3_단산24_입력필요_0넣으면_풀림(judged):
row = _row(judged, 1, 24)
(p,) = _run([{"fn": "rowDefaults", "args": [row]}])[0]
assert p["키"] == "GC000211" and set(p["필요"]) == {"높이감률", "구멍땜"}
got = _run(
[
{"fn": "missing", "args": [p]},
{"fn": "setInput", "args": [p, "높이감률", "0"]},
{"fn": "setInput", "args": [{"$": 1}, "구멍땜", "0"]},
{"fn": "missing", "args": [{"$": 2}]},
]
)
assert set(got[0]) == {"높이감률", "구멍땜"} and got[3] == []
assert got[2]["입력값"]["높이감률"] == 0 and got[2]["출처"]["구멍땜"] == {"길": "사용자"}
assert got[2]["입력값"]["유형"] == "보통" and got[2]["입력값"]["사용횟수"] == "4회"
def test_옮기기몸_확인한것만_막힘_필요걷음_배(judged):
r24, r47 = _row(judged, 1, 24), _row(judged, 1, 47)
steps = [
{"fn": "emptyDraft", "args": []},
{"fn": "rowDefaults", "args": [r24]},
# 단산24 = 막는 칸 남은 채 확인 → 막힘
{"fn": "putLine", "args": [{"$": 0}, 1, r24["차례"], {"고른": {"$": 1}, "확인": True}]},
{"fn": "rowDefaults", "args": [r47]},
{"fn": "setInput", "args": [{"$": 3}, "구분", "토목", "B5"]},
# 단산47 은 확인 안 함 → 몸에 없음
{"fn": "putLine", "args": [{"$": 2}, 1, r47["차례"], {"고른": [], "확인": False}]},
{"fn": "rowDefaults", "args": [judged[1][0]]},
{"fn": "putLine", "args": [{"$": 5}, 1, 0, {"고른": {"$": 6}, "확인": True, "배": 1.5}]},
{"fn": "moveBody", "args": [{"$": 7}, {"layer": "personal", "owner": "u1"}]},
]
body = _run(steps)[-1]
assert body["수"] == 1
assert body["body"]["층"] == "personal" and body["body"]["주인"] == "u1"
(moved,) = body["body"]["호표"]["1"]["0"]
assert moved["배"] == 1.5 and "필요" not in moved
assert body["막힘"] == [
{"호표": 1, "차례": r24["차례"], "까닭": "입력 필요", "칸": ["높이감률", "구멍땜"]}
]
def test_고름바꾸면_확인풀림_칠(judged):
row = judged[1][0]
got = _run(
[
{"fn": "emptyDraft", "args": []},
{"fn": "rowDefaults", "args": [row]},
{"fn": "putLine", "args": [{"$": 0}, 1, 0, {"고른": {"$": 1}, "확인": True}]},
{"fn": "rowTone", "args": [row, {"고른": {"$": 1}, "확인": True}]},
{"fn": "editLine", "args": [{"$": 2}, 1, row, []]},
{"fn": "rowTone", "args": [judged[1][4], None]},
{"fn": "rowTone", "args": [judged[1][1], None]},
]
)
assert got[3] == "success"
assert got[4]["호표"]["1"]["0"]["확인"] is False
assert got[5] == "danger" and got[6] == "info"
def test_블록칠_새판정(monkeypatch):
"""SheetMap 블록 칠 = 새 판정(제안 칠) · Marks = `Judge.judged` 저장본 › 셈 중이면 옛 판정."""
from M02_MasterTemplete import M02_Hopyo_Marks as mk
from resources.tester.test_779_4_sheet_map import _node
def mark(v, n=None, call=(1,)):
return {
"번": 1,
"판정": v,
"고를수": n,
"부름": list(call),
"SK": None,
"갈래": None,
"못옮김": [],
}
got = _node(
{
"blockTones": [
mark("기초로 풂"),
mark("고를 것", 3),
mark("줄 조합", call=()),
{**mark("고를 것", 2), "SK": "SK-1", "갈래": "기초"},
mark("맞음"),
]
}
)["blocks"]
assert [b["tone"] for b in got] == ["success", "info", "info", "success", "success"]
assert got[1]["글"] == "제안 — 고를 것 3" and got[2]["글"] == "제안 — 줄 조합 · 일위대가 밖"
monkeypatch.setattr(
mk.hjudge, "judged", lambda folder, key: {10: {"판정": "기초로 풂", "고를수": 0}}
)
assert mk._judged(Path("."), "x") == {10: {"판정": "기초로 풂", "고를수": 0}}
def test_엑셀보기_이음_가운데페이지없음():
"""줄 판 = 시트 아래 `[data-hopyo-lines]` · [옮기기] = 오른쪽 판 아래 · 단가산출 목록 = 새 블록 판정."""
assert not (M02 / "M02_MasterTemplete_Hopyo_Wizard.ts").exists()
sheet = (M02 / "M02_MasterTemplete_Hopyo_Sheet.ts").read_text(encoding="utf-8")
assert "createJudgeRows({" in sheet and "createJudgeMove({" in sheet
right = sheet.split('className: "m02-hopyo-sheet__side"')[1].split("});")[0]
assert "move.root" in right
sk = (M02 / "M02_MasterTemplete_Hopyo_SheetSk.ts").read_text(encoding="utf-8")
assert "createJudgeBlock({" in sk and "sangeunStep" not in sk