feat(master_data): 건설품셈 공통 6장 로직 60개 — 철근콘크리트공사

- 로직_건설품셈_공통6장.json: 콘크리트 · 철근 · 거푸집 · PSC · 교량 가설·부대공 · 조립식 구조물
- 일당 편성 표 = 편성 ÷ 시공량 · 기계 1대 × 8시간 ÷ 시공량 · 공구손료는 인력품(작업조 노임)의 %
- 8장: 기계 고르기 더함 · 조종원직종 입력 로직 새로(고소작업차 — 원문 운전사 구분에 이름 없음)
- 재료_자체: 에폭시 접착제 · 폼타이 규격 · 강재거푸집 · 문양거푸집 · 원형맨홀거푸집 · 쉬즈관
- 식 엔진: 맞는 줄이 여럿이면 찾는 값 칸이 있는 줄만 · 괄호 든 칸 이름은 작은따옴표
- 막힘: 6-2-5(절단공 노임 요소 없음) · 6-3-6 해체(특수비계공 노임 요소 없음)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V1MKKZKpUHTPKb513FneU8
This commit is contained in:
2026-09-19 17:42:08 +09:00
co-authored by Claude Opus 5
parent 380f1bcef7
commit c68e26e882
6 changed files with 5763 additions and 12 deletions
+1
View File
@@ -112,6 +112,7 @@
- 요소 — `그룹:원문:열쇠` · 값 묶음은 `.칸``기계:건설품셈:0201-0070.주연료` · 둘째 쌍점 뒤는 모두 열쇠(`재료:오피넷유가:경유:전국평균`).
- 열쇠에 `{이름}` — 입력·중간 값을 끼움 — `기계:건설품셈:{기계}.가격`
- 표 찾기 — `찾기(소요량:산림품셈:13-4-1 메쌓기, 뒷길이=뒷길이, 돌=돌, 쌓기=쌓기).석공`
- 맞는 줄이 여럿이면 찾는 값 칸이 있는 줄만 봄. 칸 이름에 괄호·빈칸이 있으면 작은따옴표 — `.'시공량(m)'`
- 찾기·로직 인자의 글자 값은 작은따옴표 — `구분='V(다짐속도,km/hr)'` · 따옴표 없으면 이름(입력·중간).
- 로직 부르기 — `로직(건설품셈:8 굴착기(무한궤도) 시간당 경비, 기계='0201-0070')` · 돈 로직의 결과는 비목 묶음 `.노무비` `.재료비` `.경비` `.계` · 돈 아닌 로직은 수 하나.
- 입력 · 중간 · 비목 합은 이름 그대로.
@@ -104,9 +104,16 @@ class _Parser:
return ("neg", self.unary())
node = self.primary()
while self.take("."):
node = ("field", node, self.name())
node = ("field", node, self.quoted() if self.take("'") else self.name())
return node
def quoted(self) -> str:
end = self.s.find("'", self.i)
if end < 0:
self.fail("작은따옴표 안 닫힘")
text, self.i = self.s[self.i : end], end + 1
return text
def name(self) -> str:
self.ws()
m = _NAME.match(self.s, self.i)
@@ -267,7 +274,7 @@ def _match(table: dict, row: dict, cond: str, want) -> bool:
return have == want
def find(table: dict, conds: dict) -> dict:
def find(table: dict, conds: dict, want: str | None = None) -> dict:
for cond in conds:
if cond not in table.get("조건", {}):
raise FormulaError(f"{table['열쇠']}」 에 없는 조건 「{cond}")
@@ -277,6 +284,8 @@ def find(table: dict, conds: dict) -> dict:
ups = [r[c][1] for r in hits if table["조건"].get(c) == "범위" and r.get(c)]
if ups and None not in ups:
hits = [r for r in hits if not r.get(c) or r[c][1] == min(ups)]
if len(hits) > 1 and want: # 여러 줄이면 찾는 값 칸이 있는 줄만
hits = [row for row in hits if want in row]
if len(hits) != 1:
raise FormulaError(f"{table['열쇠']}{conds} 맞는 줄 {len(hits)}")
return hits[0]
@@ -298,7 +307,12 @@ def evaluate(node, env: dict, master: Master, depth: int = 0):
raise FormulaError(f"{node[1]}」 값이 범위 — 입력으로 받아야 함")
return value
if kind == "field":
base = evaluate(node[1], env, master, depth)
if node[1][0] == "find":
table = master.get(fill(node[1][1], env))
conds = {k: evaluate(v, env, master, depth) for k, v in node[1][2]}
base = find(table, conds, node[2])
else:
base = evaluate(node[1], env, master, depth)
if not isinstance(base, dict) or node[2] not in base:
raise FormulaError(f"칸 「{node[2]}」 없음")
value = base[node[2]]
File diff suppressed because it is too large Load Diff
@@ -69,7 +69,7 @@
"출처": "건설품셈 공통 8-1-5 · 8-1-6",
"소유": "공용",
"입력": [
{ "이름": "기계", "고르기": ["1730-0015", "7204-0055", "0602-0150", "2702-0020", "0101-0007", "0101-0010", "0101-0012", "0101-0019", "0101-0032", "2105-0005", "2104-0010", "2104-0015", "2104-0020", "2104-0025", "2104-0030", "2104-0035", "2104-0040", "2104-0045", "2104-0050", "2104-0060", "2104-0070", "2104-0080", "2104-0100", "2104-0130", "2104-0160", "2104-0200", "2104-0220", "2104-0250", "2104-0300", "4504-0032", "4504-0036", "4504-0041", "4504-0043", "4504-0047", "4504-0052", "4205-0010", "4205-0017", "4205-0020", "4205-0030", "4205-0040", "4205-0045"] },
{ "이름": "기계", "고르기": ["1730-0015", "7204-0055", "0602-0150", "2702-0020", "0101-0007", "0101-0010", "0101-0012", "0101-0019", "0101-0032", "2105-0005", "2104-0010", "2104-0015", "2104-0020", "2104-0025", "2104-0030", "2104-0035", "2104-0040", "2104-0045", "2104-0050", "2104-0060", "2104-0070", "2104-0080", "2104-0100", "2104-0130", "2104-0160", "2104-0200", "2104-0220", "2104-0250", "2104-0300", "4504-0032", "4504-0036", "4504-0041", "4504-0043", "4504-0047", "4504-0052", "4205-0010", "4205-0017", "4205-0020", "4205-0030", "4205-0040", "4205-0045", "2105-0002", "2105-0003", "2105-0010", "2105-0015", "2105-0018", "2502-0025", "0602-0025", "2502-0050", "2101-0010", "2101-0015", "2101-0020", "2101-0025", "2101-0030", "2101-0035", "2101-0040", "2101-0050", "2101-0070", "2101-0080", "2101-0100", "2101-0150", "2101-0220", "2101-0280", "2101-0300"] },
{
"이름": "지역",
"고르기": [
@@ -304,10 +304,7 @@
"입력": [
{
"이름": "기계",
"고르기": [
"7620-0003",
"7730-0150"
]
"고르기": ["7620-0003", "7730-0150", "6516-0060", "6516-0120", "6516-0250", "6516-0300", "6105-0190", "6202-0060", "0230-0002"]
}
],
"중간": [
@@ -328,6 +325,79 @@
],
"끝수": "손료 원 미만 버림(건설품셈 공통 8-1-5 12)",
"비고": "원문 운전경비 표에 주연료·조종원이 없는 기종(절단기 · 펌프) — 동력비는 원문대로 별도"
},
{
"열쇠": "8 기계 시간당 경비(조종원직종 입력)",
"이름": "기계 시간당 기계경비(조종원직종 입력)",
"결과단위": "원/시간",
"출처": "건설품셈 공통 8-1-5 · 8-1-6",
"소유": "공용",
"입력": [
{
"이름": "기계",
"고르기": ["2106-0002", "2106-0003", "2106-0005"]
},
{
"이름": "지역",
"고르기": ["전국평균", "전국", "서울", "경기", "강원", "충북", "충남", "전북", "경북", "경남", "부산", "제주", "대구", "인천", "대전", "울산", "세종", "전남광주"],
"출처": "건설품셈 공통 8-1-7 5"
},
{
"이름": "조종원직종",
"고르기": ["1048", "1049", "1050"],
"출처": "건설품셈 공통 8-1-3 5",
"비고": "원문 운전사 구분에 기종 이름 없음 — 설계자가 고름(1048 건설기계운전사 · 1049 화물차운전사 · 1050 일반기계운전사)"
}
],
"중간": [
{
"이름": "손료",
"식": "버림(기계:건설품셈:{기계}.가격 * 1000 * ((기계:건설품셈:{기계}.상각비계수 + 기계:건설품셈:{기계}.정비비계수) + 기계:건설품셈:{기계}.관리비계수) / 10000000, 0)",
"출처": "건설품셈 공통 8-1-5 12 · 8-1-6 4"
},
{
"이름": "연료",
"식": "기계:건설품셈:{기계}.주연료종류",
"출처": "건설품셈 공통 8-4 주①㉰"
}
],
"호표": [
{
"종류": "재료",
"요소": "재료:오피넷유가:{연료}:{지역}",
"이름": "주연료",
"단위": "",
"수량": "기계:건설품셈:{기계}.주연료",
"비목": "재료비",
"출처": "건설품셈 공통 8-4-1"
},
{
"종류": "인력",
"요소": "인력:건설노임:{조종원직종}",
"이름": "조종원",
"단위": "인",
"수량": "기계:건설품셈:{기계}.조종원 / 8",
"비목": "노무비",
"출처": "건설품셈 공통 8-1-3 6",
"비고": "환산 근거 미확인 — 우선 하루 ÷ 8"
}
],
"덧줄": [
{
"이름": "잡재료비",
"식": "재료비 * 기계:건설품셈:{기계}.잡재료율 / 100",
"비목": "재료비",
"출처": "건설품셈 공통 8-4 주②"
},
{
"이름": "기계손료",
"식": "손료",
"비목": "경비",
"출처": "건설품셈 공통 8-1-6 2"
}
],
"끝수": "손료 원 미만 버림(건설품셈 공통 8-1-5 12) · 합계 원문 규정 없음",
"비고": "원문 8-1-3 5 운전사 구분에 이름 없는 기종 — 조종원직종을 설계자가 고름"
}
]
}
+16 -4
View File
@@ -5,9 +5,9 @@
"비고": "공시에 없는 재료 — 이름·규격·단위만 · 값은 관리자가 물가지·견적으로 채움",
"줄": [
{ "열쇠": "레미콘", "이름": "레디믹스트콘크리트", "규격": "", "단위": "㎥", "값": null, "출처": "자체", "비고": "산림품셈 12-9-1 · 12-11-1 · 12-11-2-1 · 12-12 · 12-13 · 12-15 · 12-16 · 12-17-2 · 12-17-3 · 13-4-4 주① · 13-4-5 · 13-5-1 주⑤ · 13-5-2 · 규격(강도·골재·슬럼프)별 값이 다르면 관리자가 줄을 나눔" },
{ "열쇠": "시멘트", "이름": "시멘트", "규격": "", "단위": "kg", "값": null, "출처": "자체", "비고": "산림품셈 12-1-3 주⑤⑥ · 12-17-1-2 주③" },
{ "열쇠": "시멘트", "이름": "시멘트", "규격": "", "단위": "kg", "값": null, "출처": "자체", "비고": "산림품셈 12-1-3 주⑤⑥ · 12-17-1-2 주③ · 건설품셈 공통 6-3-1 주⑤" },
{ "열쇠": "모래(중량)", "이름": "모래", "규격": "건조상태", "단위": "kg", "값": null, "출처": "자체", "비고": "산림품셈 12-1-3 주⑤" },
{ "열쇠": "모래", "이름": "모래", "규격": "", "단위": "㎥", "값": null, "출처": "자체", "비고": "산림품셈 12-11-3 · 12-17-1-2 주③" },
{ "열쇠": "모래", "이름": "모래", "규격": "", "단위": "㎥", "값": null, "출처": "자체", "비고": "산림품셈 12-11-3 · 12-17-1-2 주③ · 건설품셈 공통 6-3-1 주⑤" },
{ "열쇠": "자갈(중량)", "이름": "자갈 또는 부순돌", "규격": "", "단위": "kg", "값": null, "출처": "자체", "비고": "산림품셈 12-1-3 주⑤" },
{ "열쇠": "자갈", "이름": "자갈", "규격": "", "단위": "㎥", "값": null, "출처": "자체", "비고": "산림품셈 12-17-1-2 주③" },
{ "열쇠": "이형철근", "이름": "이형철근", "규격": "", "단위": "ton", "값": null, "출처": "자체", "비고": "산림품셈 12-15 · 12-16" },
@@ -17,7 +17,7 @@
{ "열쇠": "강봉 D32㎜", "이름": "강봉", "규격": "D32㎜, L=1000㎜", "단위": "EA", "값": null, "출처": "자체", "비고": "산림품셈 12-30-1" },
{ "열쇠": "사다리", "이름": "사다리", "규격": "∅19㎜ 아연도금 및 스테인레스", "단위": "ton", "값": null, "출처": "자체", "비고": "산림품셈 12-16" },
{ "열쇠": "고철", "이름": "고철", "규격": "", "단위": "kg", "값": null, "출처": "자체", "비고": "산림품셈 9-8-1 주4 · 12-34-3" },
{ "열쇠": "폼타이", "이름": "폼타이(Form Tie)", "규격": "D형 1/2인치", "단위": "본", "값": null, "출처": "자체", "비고": "산림품셈 12-4 주⑦" },
{ "열쇠": "폼타이", "이름": "폼타이(Form Tie)", "규격": "D형 1/2인치", "단위": "본", "값": null, "출처": "자체", "비고": "산림품셈 12-4 주⑦ · 건설품셈 공통 6-3-1 주④" },
{ "열쇠": "문양 스티로폴", "이름": "문양 스티로폴(자재비 포함)", "규격": "", "단위": "㎡", "값": null, "출처": "자체", "비고": "산림품셈 12-5" },
{ "열쇠": "커터 블레이드", "이름": "커터 블레이드", "규격": "320~400㎜", "단위": "개", "값": null, "출처": "자체", "비고": "산림품셈 12-7-1 주③" },
{ "열쇠": "물", "이름": "물", "규격": "", "단위": "", "값": null, "출처": "자체", "비고": "산림품셈 12-7-1 주④" },
@@ -70,6 +70,18 @@
{ "열쇠": "줄눈 모르타르", "이름": "줄눈메꿈 모르타르", "규격": "", "단위": "㎥", "값": null, "출처": "자체", "비고": "산림품셈 13-4-4 주④ · 13-5-1 주③" },
{ "열쇠": "틈메우기돌", "이름": "틈메우기돌", "규격": "", "단위": "㎥", "값": null, "출처": "자체", "비고": "산림품셈 13-5-1 주④" },
{ "열쇠": "철망태", "이름": "철망태(사각형 돌망태)", "규격": "", "단위": "㎥", "값": null, "출처": "자체", "비고": "산림품셈 13-11-4" },
{ "열쇠": "돌망태 채움재", "이름": "채움재(돌망태)", "규격": "", "단위": "㎥", "값": null, "출처": "자체", "비고": "산림품셈 13-11-4" }
{ "열쇠": "돌망태 채움재", "이름": "채움재(돌망태)", "규격": "", "단위": "㎥", "값": null, "출처": "자체", "비고": "산림품셈 13-11-4" },
{ "열쇠": "에폭시 신구콘크리트 접착제", "이름": "Epoxy 신구-콘크리트 접착제", "규격": "", "단위": "kg", "값": null, "출처": "자체", "비고": "건설품셈 공통 6-1-5" },
{ "열쇠": "에폭시 콘크리트고무 접착제", "이름": "Epoxy-콘크리트 고무 접착제", "규격": "", "단위": "kg", "값": null, "출처": "자체", "비고": "건설품셈 공통 6-1-5" },
{ "열쇠": "폼타이 9.5㎜", "이름": "폼타이(Form Tie)", "규격": "9.5㎜", "단위": "본", "값": null, "출처": "자체", "비고": "건설품셈 공통 6-3-1 주④" },
{ "열쇠": "폼타이 7.9㎜", "이름": "폼타이(Form Tie)", "규격": "7.9㎜", "단위": "본", "값": null, "출처": "자체", "비고": "건설품셈 공통 6-3-1 주④" },
{ "열쇠": "강재거푸집", "이름": "강재거푸집", "규격": "두께 3.2㎜", "단위": "㎡", "값": null, "출처": "자체", "비고": "건설품셈 공통 6-3-2" },
{ "열쇠": "문양거푸집 판넬", "이름": "문양거푸집(판넬)", "규격": "", "단위": "㎡", "값": null, "출처": "자체", "비고": "건설품셈 공통 6-3-4" },
{ "열쇠": "합성수지 원형맨홀거푸집 ø740", "이름": "합성수지(P.E) 원형 맨홀 거푸집", "규격": "ø740", "단위": "조", "값": null, "출처": "자체", "비고": "건설품셈 공통 6-3-5 주④" },
{ "열쇠": "합성수지 원형맨홀거푸집 ø900", "이름": "합성수지(P.E) 원형 맨홀 거푸집", "규격": "ø900", "단위": "조", "값": null, "출처": "자체", "비고": "건설품셈 공통 6-3-5 주④" },
{ "열쇠": "합성수지 원형맨홀거푸집 ø1200", "이름": "합성수지(P.E) 원형 맨홀 거푸집", "규격": "ø1200", "단위": "조", "값": null, "출처": "자체", "비고": "건설품셈 공통 6-3-5 주④" },
{ "열쇠": "합성수지 원형맨홀거푸집 ø1500", "이름": "합성수지(P.E) 원형 맨홀 거푸집", "규격": "ø1500", "단위": "조", "값": null, "출처": "자체", "비고": "건설품셈 공통 6-3-5 주④" },
{ "열쇠": "합성수지 원형맨홀거푸집 ø1800", "이름": "합성수지(P.E) 원형 맨홀 거푸집", "규격": "ø1800", "단위": "조", "값": null, "출처": "자체", "비고": "건설품셈 공통 6-3-5 주④" },
{ "열쇠": "쉬즈관", "이름": "쉬즈관", "규격": "", "단위": "m", "값": null, "출처": "자체", "비고": "건설품셈 공통 6-4-1 주④ · 6-4-2 주④" }
]
}
+17
View File
@@ -240,3 +240,20 @@ def test_로직_검사가_틀린_열쇠를_잡음(whole):
def test_돌고_도는_참조():
assert mf.find_loops({"": {""}, "": {""}}) == [["", "", ""]]
def test_찾기_값칸_있는_줄만():
table = {
"열쇠": "시험",
"조건": {"구분": "고르기", "높이": "고르기"},
"": [
{"구분": "비계공", "수량": 3},
{"높이": "4m이하", "시공량(m)": 100},
{"높이": "7m이하", "시공량(m)": 70},
],
}
assert mf.find(table, {"구분": "비계공"}, "수량")["수량"] == 3
assert mf.find(table, {"높이": "7m이하"}, "시공량(m)")["시공량(m)"] == 70
with pytest.raises(mf.FormulaError):
mf.find(table, {"높이": "7m이하"})
assert mf.parse("찾기(소요량:건설품셈:시험, 높이='7m이하').'시공량(m)'")[2] == "시공량(m)"