feat(714-1b): 장비 줄 펼침 덧칸 — 부른 기계경비 로직 계산서 줄 · 시간당 소계 · 계 · 키 · 인자
- master_sheet_line 장비(기계경비) 줄에 「펼침」 = {키, 이름, 단위, 입력, 줄[비목 · 이름 · 글 · 금액], 소계, 계} · 못 셈 = 까닭
- 셈 결과(master_text.lines)를 그대로 실음 — 조종원 환산 같은 규칙을 여기 두지 않음
- annotate 가 files 를 받음(펼침 계산서) · M01 /text · M02 /logic-lib/sheet 같은 KEYS
- 시험 test_714_1_sheet_line 펼침 · 두 길 같은 수(JSON 수 규칙)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,8 @@
|
||||
- `쓰는입력` = 그 줄 값(수량 식 · 요소 · 덧줄 식)에 쓰인 로직 입력 이름 — 중간값을 거슬러 감 ·
|
||||
덧줄이 부르는 비목 합 · 줄 금액은 거스르지 않음(율 식에 직접 쓴 입력만).
|
||||
- `묶음` · `장비` = 712-1 `master_bundle`(엔진 줄 덧칸 그대로).
|
||||
- `펼침`(장비 줄만 · 714-1b) = 부른 기계경비 로직의 계산서 줄(조종원 · 주연료 · 잡재료비 · 기계손료 …) ·
|
||||
시간당 비목 소계 · 계 · 부른 키 · 인자 — 셈 결과를 그대로 실음(환산 규칙을 여기 두지 않음).
|
||||
- `기준단위` = 그 줄 수량 식이 찾는 표의 기준(「10㎡」 · 712-3 `master_basis.basis`) · 답 머리
|
||||
`기준단위` = 로직 결과단위 분모(「10㎡」 · 여럿이면 「㎡ · ㎥」).
|
||||
- `환산근거` = 표 기준배 ↔ 결과단위 배(「10㎡당 → 1㎡당 ÷10」) · 재료 품목 단위 ↔ 줄 단위
|
||||
@@ -24,7 +26,7 @@ import master_formula as mf
|
||||
import master_text as mtx
|
||||
import master_unit as mu
|
||||
|
||||
KEYS = ("쓰는입력", "묶음", "장비", "기준단위", "환산근거")
|
||||
KEYS = ("쓰는입력", "묶음", "장비", "기준단위", "환산근거", "펼침")
|
||||
_HOLE = re.compile(r"\{([^{}]+)\}")
|
||||
_ERRORS = (mf.FormulaError, ArithmeticError, KeyError, TypeError, ValueError)
|
||||
|
||||
@@ -117,10 +119,39 @@ def _fields(master, row, exprs, env, per, line: dict, bundle: tuple[str, bool] |
|
||||
return out
|
||||
|
||||
|
||||
def annotate(master, key: str, given: dict, answer: dict) -> dict:
|
||||
def _spread(files: dict, master, item: dict, env: dict) -> dict:
|
||||
"""장비 줄이 부른 기계경비 로직 한 장 — `{키, 이름, 단위, 입력, 줄, 소계, 계}` · 못 셈 = 까닭."""
|
||||
node = mf.parse(item["요소"])
|
||||
key = node[1]
|
||||
try:
|
||||
key = mf.fill(key, env) if "{" in key else key
|
||||
given = {k: mf.evaluate(v, env, master, 1) for k, v in node[2]}
|
||||
got = mtx.lines(files, key, given)
|
||||
except (*_ERRORS, mf.EmptyInput) as e:
|
||||
return {"키": key, "까닭": str(e) or type(e).__name__}
|
||||
if not got.get("ok"):
|
||||
return {"키": key, "입력": given, "까닭": got.get("reason")}
|
||||
groups = got.get("groups") or []
|
||||
return {
|
||||
"키": key,
|
||||
"이름": master.logic(key).get("이름") or "",
|
||||
"단위": _per_text(mbs.result_per(master.logic(key).get("결과단위"))) or None,
|
||||
"입력": given,
|
||||
"줄": [
|
||||
{"비목": g["비목"], **{k: one.get(k) for k in ("이름", "글", "금액")}}
|
||||
for g in groups
|
||||
for one in g["줄"]
|
||||
],
|
||||
"소계": {g["비목"]: g.get("소계") for g in groups},
|
||||
"계": got.get("계"),
|
||||
}
|
||||
|
||||
|
||||
def annotate(files: dict, key: str, given: dict, answer: dict) -> dict:
|
||||
"""`master_text.lines` 답에 줄마다 `KEYS` 덧칸 · 머리 `기준단위` — 못 셈이면 답 그대로."""
|
||||
if not answer.get("ok") or "groups" not in answer:
|
||||
return answer
|
||||
master = mf.Master(files)
|
||||
row = master.logic(key)
|
||||
per = mbs.result_per(row.get("결과단위"))
|
||||
answer["기준단위"] = _per_text(per) or None
|
||||
@@ -148,6 +179,8 @@ def annotate(master, key: str, given: dict, answer: dict) -> dict:
|
||||
parts = [item.get("비목")]
|
||||
exprs = [item.get("수량"), item.get("요소")]
|
||||
got = _fields(master, row, exprs, env, per, line, mb.bundle(master, item, env))
|
||||
if got.get("장비"):
|
||||
got["펼침"] = _spread(files, master, item, env)
|
||||
for part in parts:
|
||||
target = next(slots.get(part, iter(())), None)
|
||||
if target is not None:
|
||||
|
||||
Reference in New Issue
Block a user