feat(m01): /text 재료 줄 요소조각 = 잡힌 품목 이름 · MT 참조 (PLAN 8-5)
- 검색어로 찾은 줄 · 고름으로 바꾼 줄 · 대표 줄 모두 — 글 = 자재품목 이름(지역 포함) · 참조 = MT · 그 키 - 새 master_text_item.py(700줄 안 넘기려 나눔) · 기계 · 인력 요소조각 그대로 · 금액 변화 0 - 시험 2개 추가(결속선 GF000159 · 고름) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V1MKKZKpUHTPKb513FneU8
This commit is contained in:
@@ -21,6 +21,7 @@ from decimal import ROUND_HALF_UP, Decimal
|
||||
|
||||
import master_copy as mc
|
||||
import master_formula as mf
|
||||
import master_text_item as mti
|
||||
|
||||
PLACES = Decimal("0.0001") # 화면 `formatNumber` 와 같음(소수 넷째 자리까지)
|
||||
PRICE_TAIL = {"인력": "노임", "기계": "손료"} # 단가 자리의 이름 꼬리(그 밖은 「단가」)
|
||||
@@ -545,9 +546,9 @@ def lines(files: dict, key: str, given: dict) -> dict:
|
||||
if result is not None:
|
||||
shown = result["줄"]
|
||||
env["줄"] = {r["이름"]: r["금액"] for r in shown if r["이름"]}
|
||||
for item, line, label in zip(items, shown, labels):
|
||||
for n, (item, line, label) in enumerate(zip(items, shown, labels)):
|
||||
조각 = _formula_pieces(item["수량"], env, master, name_of, alias_by_name)
|
||||
요소 = _item_pieces(item, env, master)
|
||||
요소 = _item_pieces(item, env, master, line, n)
|
||||
for part, money in _shares(line):
|
||||
split = part if len(line["비목"]) > 1 else None
|
||||
one = {
|
||||
@@ -565,14 +566,14 @@ def lines(files: dict, key: str, given: dict) -> dict:
|
||||
끝수 = _cut_note(row)
|
||||
계 = result["계"]
|
||||
else:
|
||||
for item, label in zip(items, labels):
|
||||
for n, (item, label) in enumerate(zip(items, labels)):
|
||||
parts = (
|
||||
list(mf.COST_ITEMS)
|
||||
if item.get("종류") == "로직" and "비목" not in item
|
||||
else [item.get("비목")]
|
||||
)
|
||||
조각 = _formula_pieces(item["수량"], env, master, name_of, alias_by_name)
|
||||
요소 = _item_pieces(item, env, master)
|
||||
요소 = _item_pieces(item, env, master, None, n)
|
||||
for part in parts:
|
||||
if part not in mf.COST_ITEMS:
|
||||
continue
|
||||
@@ -628,13 +629,16 @@ def _arg_text(node, env: dict, master) -> str:
|
||||
return fmt(value)
|
||||
|
||||
|
||||
def _item_pieces(item: dict, env: dict, master) -> list[dict]:
|
||||
def _item_pieces(
|
||||
item: dict, env: dict, master, line: dict | None = None, index: int = 0
|
||||
) -> list[dict]:
|
||||
"""줄의 단가 자리(요소 · 로직 부르기) 조각 — PLAN 3-1 요소조각."""
|
||||
ref = item.get("요소")
|
||||
if item.get("종류") != "로직":
|
||||
name = str(item.get("이름") or "")
|
||||
if isinstance(ref, dict): # 재료 고르기 조건 — 품명만
|
||||
return [{"kind": "요소", "글": name}]
|
||||
if isinstance(ref, dict): # 재료 고르기 조건 — 잡힌 품목 이름 · 참조(고름 · 검색어 · 대표)
|
||||
found = mti.piece(item, line, index, env, master)
|
||||
return [found or {"kind": "요소", "글": name}]
|
||||
piece = _elem_piece(master, ref, env)
|
||||
return [{**piece, "글": name or piece["글"]}]
|
||||
try:
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""재료 줄 요소조각 — 잡힌 자재품목 이름 · 참조(PLAN 8-5). `master_text._item_pieces` 가 부름."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
|
||||
import master_formula as mf
|
||||
import master_pick as mp
|
||||
|
||||
_ERRORS = (mf.FormulaError, KeyError, TypeError)
|
||||
|
||||
|
||||
def picked(item: dict, line: dict | None, index: int, env: dict, master) -> dict | None:
|
||||
"""재료 줄이 잡은 자재품목 줄 — 계산한 줄의 출처(`MT000018.거래가격`) 먼저 ·
|
||||
빈 줄이면 고름 · 검색어 · 대표로 다시 찾음. 못 찾으면 None."""
|
||||
got = str((line or {}).get("출처") or "")[:8]
|
||||
try:
|
||||
if got[:2] == "MT":
|
||||
return master.get(got)
|
||||
cond = {k: mf.fill(v, env) if isinstance(v, str) else v for k, v in item["요소"].items()}
|
||||
key = (mp.CHOSEN.get() or {}).get(str(index))
|
||||
if not key and cond.get("검색어"):
|
||||
key = mp.pick(master, cond, env, item.get("단위"))
|
||||
key = key or cond.get("대표")
|
||||
return master.get(str(key)) if key and str(key)[:2] == "MT" else None
|
||||
except _ERRORS:
|
||||
return None
|
||||
|
||||
|
||||
def piece(item: dict, line: dict | None, index: int, env: dict, master) -> dict | None:
|
||||
"""요소조각 — 글 = 품목 이름(지역 포함) · 참조 = 테이블 MT · 그 키. 못 찾으면 None."""
|
||||
row = picked(item, line, index, env, master)
|
||||
if row is None:
|
||||
return None
|
||||
key = str(row["키"])
|
||||
name = re.sub(r"\s+", " ", str(row.get("이름") or "")).strip()
|
||||
return {"kind": "요소", "글": name, "참조": {"테이블": key[:2], "키": key}}
|
||||
@@ -520,3 +520,27 @@ def test_표에_없는_입력_조합은_그_줄만_비우고_글은_그대로()
|
||||
assert len(blank) < len(lines) or got["계"] is not None
|
||||
calc = store.calc("GC001000", {**store.auto("GC001000")["값"], "시공구분": "기계시공"})
|
||||
assert got["계"] == calc["sums"]["계"]
|
||||
|
||||
|
||||
def _mat_piece(lines: list[dict], name: str) -> dict:
|
||||
return next(x for x in lines if x["이름"] == name)["요소조각"][0]
|
||||
|
||||
|
||||
def test_재료_줄_요소조각은_잡힌_품목_이름과_MT_참조() -> None:
|
||||
piece = _mat_piece(_text_of("GF000159"), "결속선 0.9㎜")
|
||||
assert piece["글"].startswith("어닐링철선(결속선)") and piece["글"].endswith("서울")
|
||||
assert piece["참조"] == {"테이블": "MT", "키": "MT000209"}
|
||||
auto = {k: v for k, v in store.auto("GF000159")["값"].items() if v is not None}
|
||||
assert store.text("GF000159", auto)["계"] == store.calc("GF000159", auto)["sums"]["계"]
|
||||
|
||||
|
||||
def test_고름으로_바꾼_재료_줄_요소조각은_고른_품목() -> None:
|
||||
import check_master as cm
|
||||
import master_pick as mp
|
||||
|
||||
auto = {k: v for k, v in store.auto("GF000159")["값"].items() if v is not None}
|
||||
items = cm.master().logic("GF000159")["호표"]
|
||||
at = next(i for i, h in enumerate(items) if h.get("이름") == "결속선 0.9㎜")
|
||||
with mp.choosing({at: "MT000174"}):
|
||||
got = _all_lines(store.text("GF000159", auto))
|
||||
assert _mat_piece(got, "결속선 0.9㎜")["참조"]["키"] == "MT000174"
|
||||
|
||||
Reference in New Issue
Block a user