- 임도품셈 판 대응표 CSV 새 자리(STmate/old/30_원자료) — 불러오기 시험 26 실패 → 0 - 가져온 식 SK = 수식 문서(멈춘 wip/802-4b-1 M02_Doc_Import 를 메인 판에 다시 얹음 · 빈 산근 = 엑셀 0 원 줄) — 줄 쌓기 「수식」 탭 길 열림 · 셈 = 단가기준(엑셀 값 기억 | 연결 값) · 화면 캐시 기록 · 수식 길 저장 `단가기준` - 1,021 블록 전수(실무 여덟 + 표본 밖 셋) 엑셀 검산 · 엑셀 전후 · 우리 전후 모두 같음 · 셈됨 줄지 않음 - 옮기기 도구 「가져온식」 상태 걷음(가져온 줄 문서로 대조) - [내 일위대가로] = 호표 하나 + 하위 → 개인 층 SC · 부르는 단산 = 가져온 식 SK(같은 내용 = 있는 것 · 못 이은 줄 = 고정) - [내 단가산출로] = 단산 블록 판 → 개인 층 가져온 식 SK(개인 층이면 [가져온 식으로 열기] 숨김) - 원문 패널(M01 로직 · M02 단가산출) 맨 위 「단가산출 기초 찾기」 · 가져온 식 SK 첫 화면 = 「어디서 왔나」 카드 (프로그램 · 파일 · 블록 · 행 · 가져온 때 · 표지 · 엑셀 글의 품셈 절 글만) - 수식 탭 [엑셀 단가 그대로 | 우리 단가로](단가산출 화면 · 저장 뒤 페이지 문서 단가기준 맞춤) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
161 lines
7.8 KiB
Python
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_Block.ts").read_text("utf-8")
|
|
assert "/hopyo-build-all" in sheet and "번: h.번" in sheet
|
|
assert 'target: { 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 # 원문 패널에 가져온 머리를 넘김
|