wip(m02-df): 서버 계산 M02_DF_Compute.py(재료 셋 · 다른 로직 · 선 규칙 · 끝수) · 설계 때 정할 것 · 다른 로직 값 길 · 시험 더함(규칙 하나 맞추는 중)

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 19:50:41 +09:00
co-authored by Claude Opus 5.5
parent 77d1bfbdc5
commit 4a62f05caf
4 changed files with 805 additions and 191 deletions
+22 -22
View File
@@ -335,16 +335,17 @@ def materials(q: str, sub: str, detail: str, page: int, size: int) -> dict:
return {"total": len(hits), "page": page, "size": size, "items": [_brief_row(r) for r in part]}
RULES = ("규격", "이름", "같음") # 규격에 값 포함 · 이름에 값 포함 · 규격 또는 이름이 값과 같음
FIELDS_RULE, OPS_RULE = ("규격", "이름"), ("포함", "같음") # Model CfMaterialRule
def slot_price(slot: dict, value: Any = None, pick: str | None = None) -> dict:
"""재료 자리 단가 — 고정 = 품목 · 선택 = 고른 품목(없으면 기본) · 조건 = 범위 + 규칙으로 찾기.
"""재료 자리 단가 — 고정 = 품목 · 선택 = 고른 품목(없으면 기본) · 조건 찾기 = 범위 + 규칙.
slot = {mode: fixed | choice | cond, ref?, default?, scope?: {구분?, 상세구분?, q?}, rule?}
조건 찾기: 하나면 그 단가 · 0 이면 「맞는 재료 없음」 · 여럿이면 몇 개인지 + 후보 다섯.
slot = Model CfElementRef 꼴 {mode: fixed | choice | rule, ref?, default?,
scope?: {구분?, 상세구분?, 찾는말?}, field?: 규격 | 이름, op?: 포함 | 같음}
조건 찾기(value = 이어진 알약 값): 하나면 그 단가 · 0 이면 맞는 재료 없음 · 여럿이면 후보 다섯.
"""
mode = slot.get("mode")
mode = slot.get("mode") or "fixed"
if mode == "fixed":
return _price("재료", str(slot.get("ref") or ""))
if mode == "choice":
@@ -352,28 +353,27 @@ def slot_price(slot: dict, value: Any = None, pick: str | None = None) -> dict:
if not ref:
return {"ok": False, "reason": "고른 품목도 기본 품목도 없음 — 품목을 골라 주세요"}
return _price("재료", str(ref))
if mode != "cond":
if mode != "rule":
return {"ok": False, "reason": f"모르는 재료 방식 「{mode}」"}
rule, scope = slot.get("rule") or "규격", slot.get("scope") or {}
if rule not in RULES:
return {"ok": False, "reason": f"모르는 찾기 규칙 「{rule}」"}
field, op, scope = (
slot.get("field") or "규격",
slot.get("op") or "포함",
slot.get("scope") or {},
)
if field not in FIELDS_RULE or op not in OPS_RULE:
return {"ok": False, "reason": f"모르는 찾기 규칙 「{field} {op}」"}
if value in (None, ""):
return {"ok": False, "reason": "찾을 값이 비어 있음 — 이어진 알약을 확인해 주세요"}
want = _squash(value)
rows = _found(
str(scope.get("구분") or ""), str(scope.get("상세구분") or ""), scope.get("q") or ""
)
fits = {
"규격": lambda r: want in _squash(r.get("규격")),
"이름": lambda r: want in _squash(r.get("이름")),
"같음": lambda r: want in (_squash(r.get("규격")), _squash(r.get("이름"))),
}[rule]
hits = [r for r in rows if fits(r)]
sub, detail = str(scope.get("구분") or ""), str(scope.get("상세구분") or "")
rows = _found(sub, detail, scope.get("찾는말") or "")
if op == "같음":
hits = [r for r in rows if _squash(r.get(field)) == want]
else:
hits = [r for r in rows if want in _squash(r.get(field))]
if not hits:
where = (
" · ".join(str(x) for x in (scope.get("구분"), scope.get("상세구분")) if x) or "전체"
)
return {"ok": False, "reason": f"맞는 재료 없음 — {where} 에서 {rule} 「{value}」"}
where = " · ".join(x for x in (sub, detail) if x) or "전체"
return {"ok": False, "reason": f"맞는 재료 없음 — {where} 에서 {field}에 「{value}」 {op}"}
if len(hits) > 1:
names = ", ".join(f"{r.get('이름')} {r.get('규격') or ''}".strip() for r in hits[:5])
return {