feat(814-1 5): 시간당 경비 계산식 기계 목록 — 원문에 있는데 빠진 기계 더함
- 기계표 630 대 가운데 어느 계산식(GC000267 ~ 276)에도 없던 384 대를 자료대로 배정(빌더 build_기계목록_빠진기계_814_1) · 연료 · 조종원 = GC000268 92 · 조종원 직종 없음 = GC000275 18 · 조종원 없음 = GC000272 43 · 연료 없음 = GC000276 7 · 손료만 = GC000273 198 · 못 넣음 50 줄(33 대) = 기계표 값 빔 · 범위(「13∼18」) — 지어 넣는 값 없음 - 로직 · 수식 문서 「기계」 목록 끝에 덧붙임(기본값 그대로) — 1,513 문서 기본 · 옵션 표본 18,797 금액 전후 같음 · 시간당 경비 595 대 셈 됨 - 조합(굴착기 + 브레이커 · 집게) = 우리 조합 식 GC000270 · 271 부름 글(`M02_Machine_Hourly.combo`) - 802-2 대조 목록 다시 지음(옵션 표본 수만 늚) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,161 @@
|
||||
"""시간당 경비 계산식(GC000267 ~ GC000276) 「기계」 고르기 · 선택 목록에 원문에 있는데 빠진 기계 더함(PLAN 814-1 · 요구 744 ~ 747).
|
||||
|
||||
기계표(EQ) 가운데 어느 계산식 목록에도 없는 기계를 자료(기계표 값)에 따라 식 하나에 배정 — 지어 넣는 값 없음:
|
||||
연료(주연료 · 종류) · 조종원(인 · 직종) · 잡재료율 · 상각 · 정비 · 관리 계수 있음 = GC000268(기계경비)
|
||||
연료 · 조종원(인) 있음 · 직종 없음 = GC000275(조종원직종 입력)
|
||||
연료 있음 · 조종원 없음 = GC000272(조종원 없는 기종)
|
||||
연료 없음 · 조종원(인 · 직종) 있음 = GC000276(연료 없는 기종)
|
||||
연료 · 조종원(직종) 없음 · 가격 · 손료계수 있음 = GC000273(손료만 — 부착 장비 등)
|
||||
그 식이 부르는 기계표 값이 비거나 수 하나가 아니면(「13∼18」) 안 넣고 「못 넣음」 목록에 적음.
|
||||
8-1-7 손료 보정 식(GC000269 · GC000274)은 보정 기종 식에 그 기계 번호가 이미 있을 때만 같이 더함.
|
||||
목록 끝에 덧붙임(기본값 그대로 = 기존 문서 금액 같음). 로직 파일(`로직_…_건설기계.json` 입력 고르기)과
|
||||
수식 문서(`수식문서/수식_…_건설기계.json` 「기계」 선택 목록)를 같이 고침. 두 번 돌려도 같음.
|
||||
|
||||
쓰기: venv python resources/master_data/scripts/build_기계목록_빠진기계_814_1.py [--check]
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
DATA = HERE.parent
|
||||
LOGIC = DATA / "로직_건설품셈_08장_건설기계.json"
|
||||
DOC = DATA / "수식문서" / "수식_건설품셈_08장_건설기계.json"
|
||||
FAMILY = [f"GC00027{i}" for i in range(6)] + ["GC000267", "GC000268", "GC000269"]
|
||||
FAMILY = sorted(set(FAMILY) | {"GC000276"})
|
||||
FIX = ("상각비계수", "정비비계수", "관리비계수")
|
||||
NEEDS = {
|
||||
"GC000268": ("가격", *FIX, "주연료종류", "주연료", "조종원직종", "조종원", "잡재료율"),
|
||||
"GC000275": ("가격", *FIX, "주연료종류", "주연료", "조종원", "잡재료율"),
|
||||
"GC000272": ("가격", *FIX, "주연료종류", "주연료", "잡재료율"),
|
||||
"GC000276": ("가격", *FIX, "조종원직종", "조종원"),
|
||||
"GC000273": ("가격", "손료계수"),
|
||||
}
|
||||
PAIR = {"GC000268": "GC000269", "GC000273": "GC000274"} # 8-1-7 보정 식
|
||||
|
||||
|
||||
TEXT = ("주연료종류", "조종원직종") # 글 칸 — 그 밖 = 수 하나(범위 「13∼18」 · 빈 값 = 없음)
|
||||
|
||||
|
||||
def _has(row: dict, field: str) -> bool:
|
||||
v = (row.get("값") or {}).get(field)
|
||||
if v in (None, ""):
|
||||
return False
|
||||
if field in TEXT:
|
||||
return bool(str(v).strip())
|
||||
try:
|
||||
return float(str(v).replace(",", "")) != 0
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
|
||||
def family_of(row: dict) -> tuple[str | None, list[str]]:
|
||||
"""기계표 한 줄 → (식 키 | None, 빈 값 이름들) — 연료 · 조종원 유무로 식 가족을 고름."""
|
||||
fuel, crew, job = _has(row, "주연료"), _has(row, "조종원"), _has(row, "조종원직종")
|
||||
if fuel and crew:
|
||||
key = "GC000268" if job else "GC000275"
|
||||
elif fuel:
|
||||
key = "GC000272"
|
||||
elif crew and job:
|
||||
key = "GC000276"
|
||||
else:
|
||||
key = "GC000273"
|
||||
miss = [f for f in NEEDS[key] if not _has(row, f)]
|
||||
return (None if miss else key), miss
|
||||
|
||||
|
||||
def _rows() -> list[dict]:
|
||||
data = json.loads((DATA / "기계.json").read_text("utf-8"))
|
||||
return [
|
||||
r for r in data.get("줄") or data.get("표") or [] if str(r.get("키", "")).startswith("EQ")
|
||||
]
|
||||
|
||||
|
||||
def _logic_lists(text: str) -> dict[str, list[str]]:
|
||||
data = json.loads(text)
|
||||
out = {}
|
||||
for row in data["줄"]:
|
||||
for i in row.get("입력") or []:
|
||||
if row.get("키") in FAMILY and i.get("이름") == "기계":
|
||||
out[row["키"]] = [str(c) for c in i.get("고르기") or []]
|
||||
return out
|
||||
|
||||
|
||||
def _span(text: str, key: str, start_pat: str) -> tuple[int, int]:
|
||||
"""그 키 블록 안 첫 `start_pat` 뒤 배열 [ … ] 의 (여는 괄호, 닫는 괄호) 자리."""
|
||||
at = text.index(f'"키": "{key}"')
|
||||
m = re.compile(start_pat).search(text, at)
|
||||
if not m:
|
||||
raise SystemExit(f"{key} 기계 목록 자리를 못 찾음")
|
||||
open_ = m.end() - 1
|
||||
close = text.index("]", open_)
|
||||
return open_, close
|
||||
|
||||
|
||||
def _append(text: str, key: str, codes: list[str], start_pat: str) -> str:
|
||||
if not codes:
|
||||
return text
|
||||
open_, close = _span(text, key, start_pat)
|
||||
body = text[open_ + 1 : close]
|
||||
if "\n" in body: # 여러 줄 꼴 — 마지막 줄 들여쓰기 그대로
|
||||
last = body.rstrip().rsplit("\n", 1)[-1]
|
||||
pad = last[: len(last) - len(last.lstrip())]
|
||||
add = "".join(f',\n{pad}"{c}"' for c in codes)
|
||||
cut = open_ + 1 + len(body.rstrip())
|
||||
return text[:cut] + add + text[cut:]
|
||||
add = "".join(f', "{c}"' for c in codes)
|
||||
return text[:close] + add + text[close:]
|
||||
|
||||
|
||||
LOGIC_PAT = r'"이름":\s*"기계",\s*"고르기":\s*\['
|
||||
DOC_PAT = r'"열쇠":\s*"기계"[^\n]*?"목록":\s*\['
|
||||
|
||||
|
||||
def plan() -> tuple[dict[str, list[str]], list[tuple[str, str, str, list[str]]]]:
|
||||
logic = LOGIC.read_text("utf-8")
|
||||
have = _logic_lists(logic)
|
||||
taken = {c for v in have.values() for c in v}
|
||||
fixes = {}
|
||||
for r in json.loads(logic)["줄"]:
|
||||
if r.get("키") in PAIR.values():
|
||||
fixes[r["키"]] = " ".join(str(x.get("식") or "") for x in r.get("중간") or [])
|
||||
add: dict[str, list[str]] = {}
|
||||
skipped = []
|
||||
for row in _rows():
|
||||
code = str(row.get("원문번호") or "")
|
||||
if not code or code in taken:
|
||||
continue
|
||||
key, miss = family_of(row)
|
||||
if key is None:
|
||||
skipped.append((code, str(row.get("이름")), str(row.get("규격") or ""), miss))
|
||||
continue
|
||||
if code in add.get(key, []):
|
||||
continue
|
||||
add.setdefault(key, []).append(code)
|
||||
if PAIR.get(key) and f"'{code}'" in fixes.get(PAIR[key], ""):
|
||||
add.setdefault(PAIR[key], []).append(code)
|
||||
return add, skipped
|
||||
|
||||
|
||||
def main(check: bool = False) -> None:
|
||||
add, skipped = plan()
|
||||
print("더함:", {k: len(v) for k, v in sorted(add.items())}, "· 못 넣음:", len(skipped))
|
||||
for code, name, spec, miss in skipped:
|
||||
print(f" 못 넣음 {code} {name} {spec} — 빈 값 {', '.join(miss)}")
|
||||
if check or not add:
|
||||
return
|
||||
logic, doc = LOGIC.read_text("utf-8"), DOC.read_text("utf-8")
|
||||
for key, codes in sorted(add.items()):
|
||||
logic = _append(logic, key, codes, LOGIC_PAT)
|
||||
doc = _append(doc, key, codes, DOC_PAT)
|
||||
json.loads(logic), json.loads(doc) # 꼴 확인
|
||||
LOGIC.write_text(logic, "utf-8", newline="")
|
||||
DOC.write_text(doc, "utf-8", newline="")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main("--check" in sys.argv)
|
||||
Reference in New Issue
Block a user