feat(m02-df): 36-2 덧 — 재료 조건 찾기 「검색어」 틀 · 표 범위 경계 규칙 칸
- Model CfMaterialRule field 「검색어」 + template(「레미콘 {a}-{b}-{c}」) + slots(자리마다 알약) · materialSources · depends · 흐름 따라가기에 자리 알약도
- Compute 재료 조건 찾기 검색어 = 틀에 자리 알약 값을 끼운 글(옛 fill)을 dv.slot_price 값으로 넘김(서버 찾기는 sub4)
- 표 찾기: 표 블록 「·범위규칙」 칸(줄마다 초과·이하 · 이상·이하 · 이상·미만 · 이하)을 Rule lookup · Compute _lookup 이 따름 · 없으면 초과·이하
- 변환: 재료 검색어 자리 = 검색어 틀 · scope 구분 · 상세구분 · 조건 찾기 요소로 들어가는 선은 안 만듦(depends 로 이음)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VBToTCGk5dqDKeXCEP3vhk
This commit is contained in:
@@ -126,7 +126,10 @@ def _element_value(
|
||||
"field": rule.get("field"),
|
||||
"op": rule.get("op"),
|
||||
}
|
||||
got = dv.slot_price(slot, ports.get(src.get("box"), {}).get(src.get("name")))
|
||||
value = ports.get(src.get("box"), {}).get(src.get("name"))
|
||||
if rule.get("field") == "검색어": # 틀의 자리마다 알약 값 → 채운 검색어 글(빈 자리면 None)
|
||||
value, slot["unit"] = _fill_template(rule, ports), el.get("unit")
|
||||
got = dv.slot_price(slot, value)
|
||||
if not got["ok"] and el.get("default"):
|
||||
errors.append(
|
||||
f"「{box.get('name')} · {el.get('name')}」 {got['reason']} — 기본 품목으로 셈"
|
||||
@@ -153,6 +156,20 @@ def _element_value(
|
||||
return got.get("value")
|
||||
|
||||
|
||||
def _fill_template(rule: dict, ports: dict) -> str | None:
|
||||
"""재료 검색어 틀의 자리에 slots 알약 값 — 옛 엔진 fill 그대로(빈 자리 = None)."""
|
||||
env = {}
|
||||
for x in rule.get("slots") or []:
|
||||
src = x.get("from") or {}
|
||||
env[x.get("name")] = ports.get(src.get("box"), {}).get(src.get("name"))
|
||||
if any(v is None for v in env.values()):
|
||||
return None
|
||||
try:
|
||||
return store.cm.mf.fill(str(rule.get("template") or ""), env)
|
||||
except store.cm.mf.FormulaError:
|
||||
return None
|
||||
|
||||
|
||||
def _port(el: dict) -> str:
|
||||
"""요소 알약 이름 — Model `elementPort`(고정 아니면 자리 이름 slot · 없으면 name)."""
|
||||
slot = str(el.get("slot") or "").strip()
|
||||
@@ -342,6 +359,7 @@ def _lookup(rule: dict, block: Any, conds: dict) -> tuple[Any, str | None]:
|
||||
if out not in head and not two:
|
||||
return None, "오류"
|
||||
tests, ups = [], []
|
||||
rk = head.index(RANGE_RULE) if RANGE_RULE in head else None
|
||||
for c in rule.get("conds") or []:
|
||||
col, want = str(c.get("col")), _cond_of(conds, c.get("from") or {})
|
||||
if col in head:
|
||||
@@ -359,10 +377,17 @@ def _lookup(rule: dict, block: Any, conds: dict) -> tuple[Any, str | None]:
|
||||
low, high = _num_of(row[lo]), _num_of(row[hi])
|
||||
if low is not None and low == high: # 숫자 하나(아래 = 위) = 같음(브레인 정함)
|
||||
return x is not None and x == low
|
||||
word = str(row[rk] or "") if rk is not None else "" # 경계 — 이상 · 미만(Rule 같음)
|
||||
return (
|
||||
x is not None
|
||||
and (row[lo] is None or (low is not None and low < x))
|
||||
and (row[hi] is None or (high is not None and x <= high))
|
||||
and (
|
||||
row[lo] is None
|
||||
or (low is not None and (low <= x if word[:2] == "이상" else low < x))
|
||||
)
|
||||
and (
|
||||
row[hi] is None
|
||||
or (high is not None and (x < high if word[-2:] == "미만" else x <= high))
|
||||
)
|
||||
)
|
||||
|
||||
tests.append(fits)
|
||||
@@ -444,7 +469,12 @@ def _depends(lg: dict) -> list[tuple[str, str]]:
|
||||
for b in lg.get("boxes") or []:
|
||||
for el in (b.get("input") or {}).get("elements") or []:
|
||||
if el.get("mode") == "rule" and el.get("rule"):
|
||||
out.append(((el["rule"].get("from") or {}).get("box"), b["id"]))
|
||||
rule = el["rule"]
|
||||
srcs = [
|
||||
rule.get("from") or {},
|
||||
*[x.get("from") or {} for x in rule.get("slots") or []],
|
||||
]
|
||||
out += [(p.get("box"), b["id"]) for p in srcs]
|
||||
return out
|
||||
|
||||
|
||||
@@ -482,6 +512,7 @@ def _sheet_of(box: dict, arrived: dict, given: dict) -> dict:
|
||||
|
||||
|
||||
REST = "앞 조건이 안 맞아 쉬는 갈래"
|
||||
RANGE_RULE = "·범위규칙" # 표 블록 줄마다 범위 경계 규칙 칸(Values._table · Rule RANGE_RULE)
|
||||
NA_VALUE = "#N/A" # 함수 칸으로 가는 표 찾기 실패 — 칸에 NA() · 결과 오류일 때만 끊긴 곳(Eval 같음)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user