feat(master_data): 관경이 든 재료 참조를 고르기 조건으로 · 조건 값의 입력 자리 채움
- 엔진 — 고르기 조건 값의 `{입력}` 을 계산 때 채움(관경) · 검사에서는 그 칸을 뺌(`static`)
- 흄관 · 파형강관 두 줄을 고르기 조건으로 바꿈
- VR관(자재품목 단위 「본」 · 호표 「m」 환산 필요) · 고무링 · 커플링밴드 셋은 옛 참조 유지 — 조건만으로 줄이 안 좁혀짐
- 시험 하나 더함
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JgUhN55Z3BCYhUJTjwTNfj
This commit is contained in:
@@ -479,7 +479,12 @@ def run(master: Master, ref: str, given: dict, depth: int = 0) -> dict:
|
|||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# 고르기 조건 묶음은 그대로 넘김 — `element` 가 대표 줄·첫 줄로 풂
|
# 고르기 조건 묶음은 그대로 넘김 — `element` 가 대표 줄·첫 줄로 풂
|
||||||
ref = item["요소"] if isinstance(item["요소"], dict) else fill(item["요소"], env)
|
# 조건 값의 `{입력}`(관경 등)도 채움 — 규격이 입력 따라 바뀌는 관 줄
|
||||||
|
ref = (
|
||||||
|
{k: fill(v, env) if isinstance(v, str) else v for k, v in item["요소"].items()}
|
||||||
|
if isinstance(item["요소"], dict)
|
||||||
|
else fill(item["요소"], env)
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
price, source = element(master, ref, env, item.get("단위"))
|
price, source = element(master, ref, env, item.get("단위"))
|
||||||
except FormulaError:
|
except FormulaError:
|
||||||
@@ -704,6 +709,7 @@ from master_material import ( # noqa: E402, F401
|
|||||||
candidates,
|
candidates,
|
||||||
check_pick,
|
check_pick,
|
||||||
check_unit,
|
check_unit,
|
||||||
|
static,
|
||||||
cond_text,
|
cond_text,
|
||||||
element,
|
element,
|
||||||
pick,
|
pick,
|
||||||
|
|||||||
@@ -106,6 +106,11 @@ def _candidates(master: mf.Master, ref: str, row: dict, env: dict) -> list[str]:
|
|||||||
|
|
||||||
|
|
||||||
# ── 재료 고르기 ────────────────────────────────────────────────────────
|
# ── 재료 고르기 ────────────────────────────────────────────────────────
|
||||||
|
def static(cond: dict) -> dict:
|
||||||
|
"""검사용 조건 — 값에 `{입력}` 이 든 칸은 뺌(계산 때 채워짐)."""
|
||||||
|
return {k: v for k, v in cond.items() if not (isinstance(v, str) and "{" in v)}
|
||||||
|
|
||||||
|
|
||||||
def cond_text(cond: dict) -> str:
|
def cond_text(cond: dict) -> str:
|
||||||
"""고르기 조건 한 글자 열쇠 — 「구분|상세구분|규격|대표」 · 화면 단가 미리보기가 줄을 찾는 이름."""
|
"""고르기 조건 한 글자 열쇠 — 「구분|상세구분|규격|대표」 · 화면 단가 미리보기가 줄을 찾는 이름."""
|
||||||
return "|".join(str(cond.get(k) or "") for k in PICK_KEYS)
|
return "|".join(str(cond.get(k) or "") for k in PICK_KEYS)
|
||||||
@@ -147,6 +152,7 @@ def pick(master: mf.Master, cond: dict, env: dict, unit: str | None = None) -> s
|
|||||||
def check_unit(master: mf.Master, where: str, cond: dict, unit: str | None) -> list[str]:
|
def check_unit(master: mf.Master, where: str, cond: dict, unit: str | None) -> list[str]:
|
||||||
"""단위 경고 — 호표 줄·품셈재료 줄 단위와 맞는 후보가 없거나 대표 줄 단위가 다름.
|
"""단위 경고 — 호표 줄·품셈재료 줄 단위와 맞는 후보가 없거나 대표 줄 단위가 다름.
|
||||||
틀린 데이터가 아니라 좁혀야 할 조건이라 `check_pick` 과 따로 봄(`check_master 단위`)."""
|
틀린 데이터가 아니라 좁혀야 할 조건이라 `check_pick` 과 따로 봄(`check_master 단위`)."""
|
||||||
|
cond = static(cond)
|
||||||
rows = master.index.get(MARKET, {})
|
rows = master.index.get(MARKET, {})
|
||||||
rep = str(cond.get("대표") or "")
|
rep = str(cond.get("대표") or "")
|
||||||
if not unit or set(cond) - set(PICK_KEYS) or not cond.get("구분"):
|
if not unit or set(cond) - set(PICK_KEYS) or not cond.get("구분"):
|
||||||
@@ -163,9 +169,11 @@ def check_unit(master: mf.Master, where: str, cond: dict, unit: str | None) -> l
|
|||||||
|
|
||||||
|
|
||||||
def check_pick(master: mf.Master, where: str, cond: dict, unit: str | None = None) -> list[str]:
|
def check_pick(master: mf.Master, where: str, cond: dict, unit: str | None = None) -> list[str]:
|
||||||
"""고르기 조건 검사 — 칸 이름 · 구분·상세구분이 자재품목에 있는지 · 대표 줄 · 후보 0."""
|
"""고르기 조건 검사 — 칸 이름 · 구분·상세구분이 자재품목에 있는지 · 대표 줄 · 후보 0.
|
||||||
|
값에 `{입력}` 이 든 칸(관경 등)은 계산 때 채워지므로 검사에서 뺌."""
|
||||||
if set(cond) - set(PICK_KEYS) or not cond.get("구분"):
|
if set(cond) - set(PICK_KEYS) or not cond.get("구분"):
|
||||||
return [f"{where} · 재료 고르기 조건 모양 「{cond}」 — {' · '.join(PICK_KEYS)}"]
|
return [f"{where} · 재료 고르기 조건 모양 「{cond}」 — {' · '.join(PICK_KEYS)}"]
|
||||||
|
cond = static(cond)
|
||||||
out, rows = [], master.index.get(MARKET, {})
|
out, rows = [], master.index.get(MARKET, {})
|
||||||
for slot in ("구분", "상세구분"):
|
for slot in ("구분", "상세구분"):
|
||||||
if cond.get(slot) and not any(r.get(slot) == cond[slot] for r in rows.values()):
|
if cond.get(slot) and not any(r.get(slot) == cond[slot] for r in rows.values()):
|
||||||
|
|||||||
@@ -1989,7 +1989,11 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"종류": "재료",
|
"종류": "재료",
|
||||||
"요소": "MP:흄관 ∅{관경}㎜",
|
"요소": {
|
||||||
|
"구분": "관·이음관",
|
||||||
|
"상세구분": "흄관",
|
||||||
|
"규격": "{관경}"
|
||||||
|
},
|
||||||
"이름": "흄관",
|
"이름": "흄관",
|
||||||
"단위": "m",
|
"단위": "m",
|
||||||
"수량": "찾기(QF000371, 구분='흄관', 관경=관경).적용",
|
"수량": "찾기(QF000371, 구분='흄관', 관경=관경).적용",
|
||||||
@@ -2133,7 +2137,11 @@
|
|||||||
"호표": [
|
"호표": [
|
||||||
{
|
{
|
||||||
"종류": "재료",
|
"종류": "재료",
|
||||||
"요소": "MP:파형강관 ∅{관경}㎜",
|
"요소": {
|
||||||
|
"구분": "관·이음관",
|
||||||
|
"상세구분": "파형강관",
|
||||||
|
"규격": "{관경}"
|
||||||
|
},
|
||||||
"이름": "파형강관",
|
"이름": "파형강관",
|
||||||
"단위": "m",
|
"단위": "m",
|
||||||
"수량": "찾기(QF000373, 구분='파형강관', 관경=관경).적용",
|
"수량": "찾기(QF000373, 구분='파형강관', 관경=관경).적용",
|
||||||
|
|||||||
@@ -952,3 +952,12 @@ def test_단위_경고는_로직_검사와_따로():
|
|||||||
assert cm.check_logics(files, whole) == []
|
assert cm.check_logics(files, whole) == []
|
||||||
warned = cm.check_units(files, whole)
|
warned = cm.check_units(files, whole)
|
||||||
assert warned and all("단위" in x for x in warned)
|
assert warned and all("단위" in x for x in warned)
|
||||||
|
|
||||||
|
|
||||||
|
def test_고르기_조건의_입력_자리_채움(whole):
|
||||||
|
"""관경 같은 입력이 규격에 든 고르기 조건 — 계산 때 채워지고, 검사에서는 그 칸을 뺌."""
|
||||||
|
cond = {"구분": "관·이음관", "상세구분": "파형강관", "규격": "{관경}"}
|
||||||
|
assert mm.static(cond) == {"구분": "관·이음관", "상세구분": "파형강관"}
|
||||||
|
assert mf.check_pick(whole, "시험", cond) == []
|
||||||
|
채움 = {k: mf.fill(v, {"관경": Decimal(800)}) for k, v in cond.items()}
|
||||||
|
assert mm.candidates(whole, 채움, None, "m")
|
||||||
|
|||||||
Reference in New Issue
Block a user