knowledge(M01): 값 없는 자재품목 줄 가운데 로직이 실제로 쓰는 것만 가림

- `scripts/check_값없는줄.py` 신설 — 로직 1,354 줄을 훑어 호표 직접 지정·고르기 조건 후보·
  대표 줄·품셈재료 연결(문자열 키·지역 조건)까지 실제 참조를 셈, 값은 안 채움
- 재료_자재품목.json 다섯 칸(물가자료·유통물가·물가정보·거래가격·관급) 모두 빈 줄 345 가운데
  로직이 실제로 가리키는 진짜 구멍은 31 줄 — `ref/_분석_값없는줄.md` 신설
- `ref/_관리자값_미확보.md` 재검증 — 45종 가운데 6종은 고르기 조건으로 바뀌었거나 로직이
  재료 자체를 뺀 것이라 로직이 안 씀으로 옮김, 남은 39종·로직 49개는 그대로 관리자 몫
- `ref/_없는_재료.md` 재검증 — 111줄 모두 지금도 로직이 부름, 뺄 줄 없음

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JgUhN55Z3BCYhUJTjwTNfj
This commit is contained in:
2026-09-22 12:21:41 +09:00
co-authored by Claude Sonnet 5
parent 0683da856b
commit e080649b2e
4 changed files with 193 additions and 7 deletions
@@ -0,0 +1,120 @@
# -*- coding: utf-8 -*-
"""재료_자재품목.json 다섯 값 칸이 모두 빈 줄 가운데 로직이 실제로 가리키는 줄 — 읽기 전용 분석.
· 「빈 줄」 = 물가자료·유통물가·물가정보·거래가격·관급 다섯 칸이 모두 없음
· 「로직이 가리킴」 = 로직 호표 요소가 그 키를 바로 적음 · 재료 고르기 조건(구분·상세구분·규격)의
후보 줄 · 대표 줄로 콕 집음 · 품셈재료 연결(문자열 키 · 지역 조건)을 따라간 줄
· 지역 조건(모래·자갈 등)은 실제 프로젝트 자재지역을 모르므로 지역을 안 가리고 이름·단위만 맞춤
(후보를 넓게 잡아 「안 쓰임」 판정이 헛짚지 않게)
· 값은 채우지 않음 · 마스터 파일을 고치지 않음
돌리기: ./venv/Scripts/python.exe resources/master_data/scripts/check_값없는줄.py
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
MASTER = HERE.parent
sys.path.insert(0, str(HERE))
import check_master as cm # noqa: E402
import master_material as mm # noqa: E402
PRICE = ("물가자료", "유통물가", "물가정보", "거래가격", "관급")
# 두 ref 표의 키 목록 — 로직이 지금도 부르는지 다시 잣대질할 때 씀 (`_관리자값_미확보.md`·`_없는_재료.md`)
ADMIN_KEYS = (
"MP000253 MP000290 MP000293 MP000048 MP000094 MP000216 MP000299 MP000359 "
"MP000023 MP000052 MP000055 MP000060 MP000065 MP000066 MP000070 MP000076 MP000082 "
"MP000095 MP000113 MP000129 MP000134 MP000144 MP000150 MP000169 MP000172 MP000176 "
"MP000177 MP000201 MP000202 MP000209 MP000211 MP000215 MP000218 MP000235 MP000239 "
"MP000241 MP000242 MP000250 MP000269 MP000294 MP000310 MP000314 MP000319 MP000357 "
"MP000363"
).split()
NO_ITEM_KEYS = (
"MP000012 MP000038 MP000048 MP000052 MP000055 MP000056 MP000057 MP000060 "
"MP000065 MP000070 MP000076 MP000077 MP000078 MP000082 MP000095 MP000096 MP000101 "
"MP000113 MP000129 MP000134 MP000139 MP000144 MP000148 MP000150 MP000169 MP000172 "
"MP000176 MP000177 MP000201 MP000209 MP000210 MP000211 MP000212 MP000213 MP000214 "
"MP000216 MP000218 MP000220 MP000221 MP000223 MP000235 MP000239 MP000240 MP000241 "
"MP000242 MP000250 MP000253 MP000254 MP000255 MP000257 MP000258 MP000259 MP000260 "
"MP000261 MP000262 MP000265 MP000269 MP000271 MP000272 MP000273 MP000274 MP000275 "
"MP000276 MP000280 MP000281 MP000285 MP000286 MP000287 MP000288 MP000290 MP000293 "
"MP000294 MP000295 MP000296 MP000297 MP000298 MP000299 MP000301 MP000302 MP000303 "
"MP000304 MP000305 MP000306 MP000308 MP000309 MP000310 MP000314 MP000315 MP000319 "
"MP000325 MP000326 MP000327 MP000328 MP000330 MP000334 MP000335 MP000336 MP000337 "
"MP000338 MP000340 MP000341 MP000342 MP000343 MP000344 MP000345 MP000349 MP000351 "
"MP000352 MP000353 MP000359 MP000363"
).split()
def all_empty(master) -> set[str]:
"""MT 다섯 값 칸이 모두 빈 키."""
return {
key for key, row in master.index.get(mm.MARKET, {}).items() if not mm._offers(master, key)
}
def region_reach(master, name, unit) -> set[str]:
"""MP 연결이 지역 조건(모래·자갈 등)일 때 이름 뿌리·단위만으로 넓게 잡은 후보."""
want, need = mm.plain(name), (mm.plain_unit(unit) if unit else "")
return {
key
for root, _, _, u, key in mm._market_index(master)
if root.endswith(want) and (not need or u == need)
}
def reached(master) -> tuple[set[str], set[str]]:
"""(로직이 가리키는 MT 키, 로직이 지금도 문자열로 부르는 MP 키)."""
mt_hit: set[str] = set()
mp_hit: set[str] = set()
for path in sorted(MASTER.glob("로직_*.json")):
rows = json.loads(path.read_text(encoding="utf-8")).get("", [])
for row in rows:
for h in row.get("호표") or []:
e, unit = h.get("요소"), h.get("단위")
if isinstance(e, str) and e[:2] == mm.MARKET:
mt_hit.add(e)
elif isinstance(e, dict) and "구분" in e:
mt_hit.update(mm.candidates(master, e, None, unit))
if e.get("대표"):
mt_hit.add(str(e["대표"]))
elif isinstance(e, str) and e[:2] == mm.LINKED:
mp_hit.add(e)
link = master.index.get(mm.LINKED, {}).get(e, {}).get("연결")
if isinstance(link, str) and link[:2] == mm.MARKET:
mt_hit.add(link)
elif isinstance(link, dict) and "계약종별" not in link:
mrow = master.index[mm.LINKED][e]
mt_hit.update(region_reach(master, link.get("이름"), mrow.get("단위")))
return mt_hit, mp_hit
def main() -> int:
master = cm.master()
empty = all_empty(master)
mt_hit, mp_hit = reached(master)
gap = sorted(empty & mt_hit)
print(f"MT 줄 {len(master.index.get(mm.MARKET, {}))}")
print(f"다섯 칸 모두 빈 줄 {len(empty)} · 그 가운데 로직이 가리키는 줄 {len(gap)}")
for key in gap:
row = master.get(key)
print(f" {key} {row.get('이름')} ({row.get('구분')}·{row.get('상세구분')})")
print(f"\n로직이 지금도 MP 키를 문자열로 부르는 곳 {len(mp_hit)}")
for title, keys in (
("_관리자값_미확보.md 45종", ADMIN_KEYS),
("_없는_재료.md 111줄", NO_ITEM_KEYS),
):
unused = [k for k in keys if k not in mp_hit]
print(f"{title} 가운데 로직이 이제 안 씀 {len(unused)}{unused}")
return 0
if __name__ == "__main__":
sys.stdout.reconfigure(encoding="utf-8")
sys.exit(main())