Files
Aislo/resources/tester/test_807_1_import_doc_one.py
T
eomsangdonandClaude Opus 5.5 c5067aa2b6 feat(809-1a): 일위대가 불러오기 = 경쟁사 로직 인정 · 요소만 연결 · [수정] 모달 = 일위대가 컨테이너 부품 · 기초 판정 걷음
- [수정] 모달 = Labor_Table(읽기 · 덧칸 표지 · 엑셀 금액) · 줄 = 로직(단가산출 · 일위대가) / 요소(연결됨 · 후보 · 못 이음) · 단가산출 줄 = 가져온 식 SK 수식 탭 모달(Labor_SkEdit) · 서버 hopyo-view(M02_Hopyo_View)
- 엑셀 보기 목록 · 칠 = 줄 표지(View.listing) · 단가산출 탭 = 블록 목록 + [가져온 식으로 열기] · [내 단가산출로]
- 걷음(요구 703): 판정 체계 785(Judge · Solve · Bind · San_Basis 다섯 · Make.Move · Row 1차 판정 · 화면 Check · CheckRows · Rows · Block · JudgeApi · Picks · Judge.css) · [우리 기초로 바꾸기](to-base) · 근거 기초 제안 · Clause 기초 판정
- 시험 지움: 642_2(Bind · Solve) · 642_3(Row 1차 판정) · 642_5(옮기기 Move) · 642_6(판정 화면) · 786_2(옛 [수정] 모달) · 793_3a(근거 → 기초 제안) — 모두 걷은 기초 판정 길 몫
- 시험 고침: 642_1 · 794_2a2 · 794_2b(근거 제안 부분만 뺌) · 793_2 · 793_3b(기초 바꾸기 기대 → 없음) · 627 · 779_2 · 785_4 · 807_1 · m02_hopyo_excel · grid · stmate(judge 길 → view 길)
- 새 시험 test_809_1_import_labor(기초 길 0 · 표지 1,350 줄 · 가져온 식 SK · 모달 = Labor_Table · 금액 = 엑셀)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-08 19:17:58 +09:00

161 lines
7.8 KiB
Python

# ruff: noqa: F811 — 임시 층 fixture(world)를 test_m02_labor_ref 에서 받아 씀
"""807-1 엑셀 불러오기 = 바뀐 구조(요구 699) — 임시 storage · M01 그대로.
① 임도품셈 판 대응표 CSV 새 자리(폴더 재편)
② 단가산출 하나 = 개인 층 가져온 식 SK → 수식 길(줄 쌓기 「수식」 탭) 읽힘 · 금액 = 엑셀 ·
[우리 단가로] · [엑셀 단가 그대로] = 수식 길 저장 `단가기준`
③ 일위대가 하나 = 개인 층 SC + 부르는 단산 = 가져온 식 SK(중복이면 있는 것) · 금액 = 엑셀
④ 원문 패널 「어디서 왔나」 카드 · 품셈 절 글(TS `originOf`) · 기초 찾기 칸 이음
실무 엑셀 없으면 그 시험만 skip.
"""
from __future__ import annotations
import json
import subprocess
from pathlib import Path
import helper_m02_hopyo_golden as golden
import pytest
from M02_MasterTemplete import M02_Doc_Import as dim
from M02_MasterTemplete import M02_Doc_Sk_Path as path
from M02_MasterTemplete import M02_Hopyo_Build as hb
from M02_MasterTemplete import M02_Hopyo_Clause as clause
from M02_MasterTemplete import M02_Hopyo_Excel as hx
from M02_MasterTemplete import M02_Hopyo_Store as hs
from M02_MasterTemplete import M02_MasterTemplete_Store as store
from M02_MasterTemplete import M02_Sangeun_FromHopyo as fh
from resources.tester.test_m02_labor_ref import world # noqa: F401 — 같은 임시 층
ROOT = Path(__file__).resolve().parents[2]
def test_road_map_csv_new_place() -> None:
assert clause._ROAD_MAP.is_file() and "STmate/old/30_원자료" in clause._ROAD_MAP.as_posix()
assert clause.road_map()
def _hp(world, tag: str = "북부청임도") -> tuple[Path, str]:
src = golden.FILES.get(tag)
if src is None or not src.exists():
pytest.skip(f"실무 엑셀 없음 {tag}")
folder, data = world["personal"], src.read_bytes()
with store.at(folder):
parsed = hx.read_book(src.name, data)
row = hs.save(folder, src.name, parsed, {"이름": "sub3임시"}, None, hs.fingerprint(data))
fh.keep(folder, row["키"], src.name, data)
return folder, row["키"]
def _sk(key: str) -> dict:
return store.read("sangeun", key)["문서"]
# ── ② 단가산출 하나 ─────────────────────────────────────────────────────
def test_one_sangeun_formula_route_and_basis(world) -> None:
folder, hp = _hp(world)
with store.at(folder):
made = fh.make(folder, hp, 5) # 단산5 콘테이너운반 — 기계 시간당 이음
key = made["이름"]
doc = _sk(key)
assert dim.is_imported(doc) and doc["가져온"]["원본"]["가져온때"]
read = path.read(key) # 줄 쌓기 「수식」 탭 길(옛 400 「802-4b 몫」 없어짐)
assert read["사본"]["단가기준"] == "엑셀"
assert float(read["셈"]["계"]) == pytest.approx(float(made["검산"]["엑셀"]["계"]), abs=1)
assert read["연결"] is not None
ours = path.save(key, {"단가기준": "우리", "판": read["판"]})
assert ours["사본"]["단가기준"] == "우리" and ours["셈"]["셈됨"] is True
back = path.save(key, {"단가기준": "엑셀", "판": ours["판"]})
assert back["셈"]["계"] == read["셈"]["계"]
with pytest.raises(store.StoreError):
path.save(key, {"단가기준": "남의", "판": back["판"]})
# ── ③ 일위대가 하나 ─────────────────────────────────────────────────────
def test_one_hopyo_to_personal_sc_and_sks(world) -> None:
folder, hp = _hp(world)
with store.at(folder):
got = hb.build_all(folder, folder, hp, None, True, 3) # 제3호표 임도표지석 — 단산 셋
assert [x["번"] for x in got["만듦"]] == [3] and got["같음"] == 1 and not got["실패"]
sks = got["단가산출"]
assert len(sks) == 3 and all(dim.is_imported(_sk(k)) for k in sks)
sc = store.read("precombo", got["만듦"][0]["키"])["문서"]
refs = {(x.get("참조") or {}).get("키") for x in sc["담은로직"]}
assert set(sks) <= refs # SC 줄 = 그 SK 참조
again = hb.build_all(folder, folder, hp, None, True, 3) # 다시 = 있는 것(그대로 · 같은 SK)
assert again["그대로"] == 1 and sorted(again["단가산출"]) == sorted(sks)
assert len(list((folder / "sangeun").glob("SK-*.json"))) == 3
with pytest.raises(store.StoreError):
hb.build_all(folder, folder, hp, None, True, 9999)
# ── ④ 원문 패널 ─────────────────────────────────────────────────────────
_NODE = """
const fs = require("fs"), path = require("path");
const ts = require(path.join(process.cwd(), "config", "node_modules", "typescript"));
const at = path.join(process.cwd(), "M02_MasterTemplete", "logic", "M02_Sangeun_Origin.ts");
const js = ts.transpileModule(fs.readFileSync(at, "utf8"), {
compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2020 },
}).outputText;
const box = { exports: {} };
new Function("exports", "module", "require", js)(box.exports, box, () => ({}));
const got = JSON.parse(fs.readFileSync(0, "utf8"));
process.stdout.write(JSON.stringify(got.map((m) => box.exports.originOf(m))));
"""
def _origin(metas: list) -> list:
out = subprocess.run(
["node", "-e", _NODE],
input=json.dumps(metas, ensure_ascii=False),
capture_output=True,
text=True,
encoding="utf-8",
cwd=ROOT,
check=True,
)
return json.loads(out.stdout)
def test_origin_card_and_clauses(world) -> None:
folder, hp = _hp(world)
with store.at(folder):
meta = _sk(fh.make(folder, hp, 5)["이름"])["가져온"]
stm = {
"로직": {},
"원본": {
"갈래": "STmate",
"코드": "D02200",
"글줄": [
{"행": 3, "글": "4. 기초지정[2025년 표준품셈3-4-8]"},
{"행": 4, "글": "임도품셈 5-1-2 준용"},
{"행": 5, "글": "임도품셈 5-1-2 다시"},
],
},
}
est, st, plain = _origin([json.loads(json.dumps(meta, default=str)), stm, {"이름": "우리"}])
assert est["프로그램"] == "EST" and est["블록"] == "단산5" and est["파일"].endswith(".xls")
assert est["행"].startswith("단가산출 ") and est["때"] and est["표지"]
books = [(c["책"], c["절"]) for c in est["품셈"]]
assert ("산림표준품셈", "10-4") in books and ("건설표준품셈", "8-1-3") in books
assert st["블록"] == "D02200"
assert [(c["책"], c["절"], c["행"]) for c in st["품셈"]] == [
("표준품셈", "3-4-8", 3),
("임도품셈", "5-1-2", 4),
]
assert plain is None # 우리 SK = 카드 없음(원문 먼저)
def test_source_find_on_m01_and_m02_panels() -> None:
m02 = (ROOT / "M02_MasterTemplete/M02_MasterTemplete_Sangeun_Side.ts").read_text("utf-8")
m01 = (ROOT / "M01_MasterData/M01_MasterData_UI_LogicLab_Side.ts").read_text("utf-8")
assert "createSourceFind" in m02 and "createSourceFind" in m01
assert "originOf" in m02
sheet = (ROOT / "M02_MasterTemplete/M02_MasterTemplete_Hopyo_Mine.ts").read_text("utf-8")
block = (ROOT / "M02_MasterTemplete/M02_MasterTemplete_Hopyo_SheetSk.ts").read_text("utf-8")
assert "/hopyo-build-all" in sheet and "번: h.번" in sheet
assert '{ layer: "personal", owner: null }' in block and "openImported" in block
detail = (ROOT / "M02_MasterTemplete/M02_MasterTemplete_Sangeun_Detail.ts").read_text("utf-8")
assert "doc.가져온);" in detail # 원문 패널에 가져온 머리를 넘김