feat(M01): 흐름 로직 옛 엔진 한 곳 갈래 · 인력/기계/유가 조건 찾기 · 표 수 식 글 · 재료 키 찾기(PLAN 36-4 덧)

- master_formula.run 에 FLOW_RUN 자리 — Store_Flow 가 꽂음 · 조합 · 프로젝트 단가 · 읽는 식 · 옛 식의 로직(…) 도 흐름 로직이면 흐름 셈(단가는 마스터 · 프로젝트 사본 단가는 아직)
- 흐름 → 옛 → 흐름 돌고 도는 부름 막음 · /logic/auto 흐름 로직 = 설계 예시값
- 조건 찾기 요소 인력 · 기계 · 유가전력(group_find · 이름 · 키 · 원문번호 같음/포함 · 기본 키 대체)
- 표 값 칸 수 식 글(「(0.05)」 「1/1.30」) = 옛 엔진 파서로 수 · 재료 조건 찾기 field 키(자재 키 · 품번)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KmELpdXFDRZHhfbGuRhSmL
This commit is contained in:
2026-10-01 23:49:16 +09:00
co-authored by Claude Opus 5.5
parent 2abd3d46ae
commit 466fa56540
6 changed files with 259 additions and 13 deletions
+46 -4
View File
@@ -15,6 +15,7 @@
from __future__ import annotations
import importlib
from contextvars import ContextVar
from decimal import Decimal, InvalidOperation
from typing import Any
@@ -26,6 +27,7 @@ FLOW = "흐름"
KINDS = ("input", "function", "output")
CONVERT = "M02_MasterTemplete.DataFlow.M02_DF_Convert"
NOT_READY = "변환 준비 중"
_ACTIVE: ContextVar[tuple] = ContextVar("m01_flow_active", default=()) # 셈 중인 흐름 키
# ── 검사(Model `link` · `ruleWhy` 와 같은 뜻) ──────────────────────────────
@@ -152,9 +154,46 @@ def _value(v: Any) -> Any:
def run(flow: dict, key: str, inputs: dict) -> dict:
"""흐름 셈 — 입력 = 「설계 때 정할 것」 이름 → 값."""
given = {k: _value(v) for k, v in (inputs or {}).items()}
flow = {**flow, "key": key}
return dc.run(flow, dc._design_values(flow, given), load, flow.get("userElements") or [], [key])
active = _ACTIVE.get()
if key and key in active: # 옛 셈을 건너 다시 돌아온 흐름(흐름 → 옛 로직 → 흐름)
raise store.cm.mf.FormulaError(f"로직이 서로를 부름 — {' → '.join([*active, key])}")
token = _ACTIVE.set((*active, key))
try:
given = {k: _value(v) for k, v in (inputs or {}).items()}
flow = {**flow, "key": key}
values = dc._design_values(flow, given)
return dc.run(flow, values, load, flow.get("userElements") or [], [key])
finally:
_ACTIVE.reset(token)
def _engine(master, ref: str, row: dict, given: dict) -> dict:
"""옛 엔진 `mf.run` 갈래(조합 · 프로젝트 단가 · 읽는 식 · 옛 식의 로직(…)) — `run` 과 같은 꼴.
ponytail: 단가는 마스터에서(`M02_DF_Values`) — 프로젝트 사본 단가(master 인자)는 아직 안 씀.
"""
got = run(row[FLOW], ref, given)
if got["errors"]:
raise store.cm.mf.FormulaError(f"「{ref}」 흐름 셈 — {' · '.join(got['errors'])}")
cost = got["cost"]
kinds = {(b.get("output") or {}).get("cost") for b in row[FLOW].get("boxes") or []
if b.get("kind") == "output"} # fmt: skip
if kinds and kinds <= {"값"} and len(cost["값들"]) == 1: # 돈 아닌 로직 = 결과 하나
return {"결과": next(iter(cost["값들"].values())), "중간": {}}
return {"줄": [], **{k: cost[k] for k in dc.COSTS}, "계": cost["계"], "중간": {}}
store.cm.mf.FLOW_RUN = _engine # 옛 엔진을 부르는 곳이 모두 흐름 로직을 흐름 셈으로(한 곳 갈래)
def auto(key: str) -> dict | None:
"""견본 입력 — 흐름 로직이면 「설계 때 정할 것」 예시값 · 아니면 None(옛 자동값)."""
flow = store.logic(key)["logic"].get(FLOW)
if not isinstance(flow, dict):
return None
return {
"값": {d["port"]: (d.get("variable") or {}).get("example") for d in dc.design_inputs(flow)}
}
def calc(key: str, inputs: dict, row: dict | None = None) -> dict | None:
@@ -168,7 +207,10 @@ def calc(key: str, inputs: dict, row: dict | None = None) -> dict | None:
return None
if not isinstance(flow, dict):
return None
got = run(flow, key, inputs)
try:
got = run(flow, key, inputs)
except store.cm.mf.FormulaError as e:
return {"ok": False, "flow": True, "reason": str(e)}
names = {b["id"]: b for b in flow.get("boxes") or []}
outputs = [
{"box": i, "name": names[i].get("name"), "cost": (names[i].get("output") or {}).get("cost"),