feat(master_data): 로직 재료 줄 고르기 조건 — 구분·상세구분·규격 후보 · 대표 줄 · 후보 목록 API

- `_틀.md` 7장에 재료 고르기 문법 — 호표 `요소` 를 조건 묶음으로 · 규격 낱말 조건 · 대표 줄 지정
- 엔진 `candidates`·`pick`·`check_pick` — 조건 안 후보 목록(화면·테스트 컨테이너 공용) · 시험 계산은 대표 줄, 없으면 값 있는 첫 줄(자재지역 우선)
- 로직 검사에 고르기 조건 검사 — 칸 이름 · 자재품목에 없는 구분·상세구분 · 조건 밖 대표 · 후보 0
- M01 서버 `GET /api/m01/materials` — 구분·상세구분·규격·자재지역으로 좁힌 후보와 기본 줄
- 옛 방식 재료 줄(`MP…`·`MT…` 키)은 그대로 돎

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JgUhN55Z3BCYhUJTjwTNfj
This commit is contained in:
2026-09-20 21:33:34 +09:00
co-authored by Claude Opus 5
parent 565833bcb8
commit 999f3363db
8 changed files with 300 additions and 7 deletions
+8
View File
@@ -124,6 +124,14 @@ def get_pick(kind: str, q: str = "", limit: int = 50) -> dict:
return _call(store.pick, kind, q, limit)
@router.get("/materials")
def get_materials(
sub: str, detail: str = "", spec: str = "", region: str = "", limit: int = 50
) -> dict:
"""재료 고르기 조건 안 후보 — sub = 구분 · detail = 상세구분 · spec = 규격 · region = 자재지역."""
return _call(store.materials, sub, detail, spec, region, limit)
@router.post("/calc")
def post_calc(body: CalcBody) -> dict:
return _call(store.calc, body.key, body.inputs, body.row, body.file)
+23
View File
@@ -412,6 +412,29 @@ def pick(kind: str, q: str, limit: int) -> dict:
return {"total": len(hits), "items": hits[: min(max(limit, 1), 200)]}
def _market_brief(row: dict) -> dict:
return {
"ref": str(row.get("키")),
**{k: row.get(k) for k in ("구분", "상세구분", "이름", "규격", "단위")},
"값": _lowest(row),
"관급": row.get("관급") is not None,
}
def materials(sub: str, detail: str, spec: str, region: str, limit: int) -> dict:
"""재료 고르기 조건 안 후보 목록 — 화면·테스트 컨테이너가 같이 씀.
`기본` = 시험 계산이 쓸 줄(대표 줄이 없을 때의 첫 줄)."""
whole = cm.master(folder=FOLDER)
cond = {"구분": sub, "상세구분": detail or None, "규격": spec or None}
env = {cm.mf.REGION: region} if region else {}
keys = cm.mf.candidates(whole, cond, env)
return {
"total": len(keys),
"기본": cm.mf.pick(whole, cond, env) if keys else None,
"items": [_market_brief(whole.get(k)) for k in keys[: min(max(limit, 1), 200)]],
}
# ── 시험 계산 ──────────────────────────────────────────────────────────
def _swap(files: dict, key: str, row: dict, file: str | None) -> str:
"""저장 전 시험 — 메모리 사본에서 그 로직을 고친 줄로 바꿈(없으면 `file` 에 더함) · 돌려받는 것 = 계산할 키."""