knowledge(마스터): 나라장터 빼기 — 로직 호표를 자재품목으로 갈아 끼우고 MN 폐기

- 로직이 가리키던 나라장터 자리 121(정적 키 18 · 고르기 13벌)을 자재품목(MT) 줄로 갈아 끼움 — 이름·규격·단위가 맞는 줄 · 지역이 갈리는 품목은 서울 줄.
- 자재품목에 맞는 줄이 없는 넷은 품셈재료 미연결로 돌림 — 시너(MP000266) · 동 및 동합금용 용접봉 BCUP3 · 폴리에틸렌필름 0.04㎜ · 아스팔트싱글(새 줄 셋).
- 엔진이 자재품목 키를 바로 받으면 값 열 다섯 가운데 낮은 값(시험 계산용) · 출처는 「키.열」.
- 재료_나라장터자재.json 과 그 빌더를 old 로 옮기고 키 대장에 MN 폐기 · check_master · 키 대장 · M01 서버·화면 · _틀.md · compare_estx 의 나라장터 자리 정리.
- 끝 검사: check_master 틀 0 · 로직 0 · 일괄 시험 계산 통과 988 · 멈춤 363(앞 989 · 362 — 아스팔트싱글 한 줄) · pytest 67 통과 · typecheck 깨끗.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JgUhN55Z3BCYhUJTjwTNfj
This commit is contained in:
2026-09-20 16:49:47 +09:00
co-authored by Claude Opus 5
parent 9fe36b871d
commit b2a762d8e1
27 changed files with 439 additions and 7602 deletions
+8 -46
View File
@@ -172,61 +172,23 @@ def bare_spec(name, spec) -> str:
return "".join(ch for ch in s if ch not in "-()[]")
def spec_match(nara_spec: str, estx_spec: str) -> bool:
"""규격 같음 — 같거나, ESTX 규격(3자 이상)이 나라장터 규격 끝과 같음(앞 수식어 「아연도」 등)."""
return nara_spec == estx_spec or (len(estx_spec) >= 3 and nara_spec.endswith(estx_spec))
def spec_match(one: str, estx_spec: str) -> bool:
"""규격 같음 — 같거나, ESTX 규격(3자 이상)이 상대 규격 끝과 같음(앞 수식어 「아연도」 등)."""
return one == estx_spec or (len(estx_spec) >= 3 and one.endswith(estx_spec))
def material_pairs(estx: Estx, nara: list[dict]):
by_name = {}
def estx_names(estx: Estx) -> dict[str, list[dict]]:
by_name: dict[str, list[dict]] = {}
for r in estx.table("StuffcDat"):
if r.get("Name") and r.get("TC"):
by_name.setdefault(base_name(r["Name"]), []).append(r)
pairs = []
for n in nara:
cands = by_name.get(base_name(n["이름"])) if n.get("") else None
if not cands:
continue
s = bare_spec(n["이름"], n["규격"])
hit = next((e for e in cands if spec_match(s, bare_spec(e["Name"], e.get("Size")))), None)
if hit:
pairs.append((n, hit))
return by_name, pairs
return by_name
def material(estx: Estx, files: dict):
nara = files["재료_나라장터자재.json"][""]
by_name, pairs = material_pairs(estx, nara)
by_name = estx_names(estx)
rows = sum(by_name.values(), [])
names = {base_name(n["이름"]) for n in nara}
print(
f"## 자재 — 나라장터 {len(nara)}(품명 {len(names)}) · ESTX {len(rows)} · 품명 같음 {len(names & set(by_name))} · 품명·규격 짝 {len(pairs)}"
)
print(" TC 대 나라장터:", spread([rate(n[""], e["TC"]) for n, e in pairs]))
for i, slot in enumerate(SLOTS, 1):
got = [rate(n[""], e[f"Cost{i}"]) for n, e in pairs if e.get(f"Cost{i}")]
print(f" 슬롯{i} {slot}:", spread(got))
closest = {}
for n, e in pairs:
got = {
s: abs(rate(n[""], e[f"Cost{i}"]))
for i, s in enumerate(SLOTS, 1)
if e.get(f"Cost{i}")
}
best = min(got, key=got.get)
closest[best] = closest.get(best, 0) + 1
print(" 가장 가까운 슬롯:", closest)
for n, e in pairs[:8]:
print(
"",
n["이름"],
"|",
n["규격"],
n[""],
"",
e.get("TC"),
{s: e.get(f"Cost{i}") for i, s in enumerate(SLOTS, 1) if e.get(f"Cost{i}")},
)
print(f"## 자재 — ESTX 품목 {len(rows)}(품명 {len(by_name)}) · 품셈재료 미연결 후보")
have, near, none = [], [], []
for r in files["재료_품셈재료.json"][""]:
if r.get("연결"):