feat(master_data): 건설 토목 1장 2장 6장 소요량 옮김
- 소요량_건설품셈_토목1장(116표) · 토목2장(9표) · 토목6장(73표) — md 표 73 · 8 · 50 개 모두 + 글 속 계수 표 - 변환 스크립트 build_소요량_건설품셈_토목.py + _토목_읽기 · _토목_표 · _토목_손 · _토목_계수 - check_master 틀 · 본문 · 로직 0건 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PAdA5ThqmtVSsbzjusk1cJ
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""글 속 계산용 수 → 그룹 계수 표 (build_소요량_건설품셈_토목.py 가 씀).
|
||||
|
||||
표에 붙은 주(①…)와 비고 글에서 정규식으로 수를 읽음(손으로 적지 않음). 주 한 줄마다 계수 표 하나.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
from _md표_읽기 import R
|
||||
from _토목_표 import conds_out
|
||||
|
||||
PCT = r"([\d.]+)%"
|
||||
|
||||
|
||||
def clean(line):
|
||||
return re.sub(r"^(비고\s*-?\s*|-\s*|[①-⑳]\s*)", "", line).strip()
|
||||
|
||||
|
||||
def rows_of(line):
|
||||
"""주 한 줄 → 계수 줄들 [(구분, 값, 단위, 딸린 칸)]."""
|
||||
s = clean(line)
|
||||
out = []
|
||||
m = re.search(r"^(.+?)(?:는|은) (인력품|주재료비|재료비)의 " + PCT, s)
|
||||
if m:
|
||||
out.append((m.group(1), R(m.group(3)), "%", {"기준": m.group(2)}))
|
||||
m2 = re.search(r"(블록 정밀절단\(전동절단기\)에 의한 시공이 아닌 경우) " + PCT, s)
|
||||
if m2:
|
||||
out.append((f"{m.group(1)} — {m2.group(1)}", R(m2.group(2)), "%", {"기준": m.group(2)}))
|
||||
return out
|
||||
m = re.search(
|
||||
r"(시공량|본 품|크레인\(시간\))(?:의|을) " + PCT + r"(?:를|까지| 범위 내에서) ?(감|가산)", s
|
||||
)
|
||||
if m:
|
||||
out.append((s, R(m.group(2)), "%", {"기준": m.group(1), "가감": m.group(3)}))
|
||||
m = re.search(r"(특별인부) (\d+)인을 감", s)
|
||||
if m:
|
||||
out.append((s, R(m.group(2)), "인", {"기준": m.group(1), "가감": "감"}))
|
||||
m = re.search(r"절단품은 본 품의 " + PCT + r", 선단가공\(Beveling\) 품은 본 품의 " + PCT, s)
|
||||
if m:
|
||||
out.append(("금긋기 및 절단품", R(m.group(1)), "%", {"기준": "본 품"}))
|
||||
out.append(("선단가공(Beveling) 품", R(m.group(2)), "%", {"기준": "본 품"}))
|
||||
m = re.search(r"두께 ([\d.]+)㎝일 때 ([\d,]+)㎡당 살수량은 일반적으로 ([\d.]+)ton", s)
|
||||
if m:
|
||||
out.append(("살수량 기준 두께", R(m.group(1)), "㎝", {}))
|
||||
out.append(("살수량 기준 면적", R(m.group(2)), "㎡", {}))
|
||||
out.append(("살수량", R(m.group(3)), "ton", {}))
|
||||
m = re.search(r"보통인부 (\d+)인기준 두께 (\d+)㎝시 ([\d,]+)㎡, 두께 (\d+)㎝시 ([\d,]+)㎡", s)
|
||||
if m:
|
||||
out.append(("모래 부설 일당시공량 기준 인원", R(m.group(1)), "인", {"기준": "보통인부"}))
|
||||
out.append((f"모래 부설 일당시공량 두께 {m.group(2)}㎝", R(m.group(3)), "㎡", {}))
|
||||
out.append((f"모래 부설 일당시공량 두께 {m.group(4)}㎝", R(m.group(5)), "㎡", {}))
|
||||
m = re.search(r"굴착기\(규격 ([\d.]+)㎥, 사용시간 ([\d.]+)hr\)", s)
|
||||
if m:
|
||||
out.append(("크레인 대체 굴착기 규격", R(m.group(1)), "㎥", {}))
|
||||
out.append(("크레인 대체 굴착기 사용시간", R(m.group(2)), "hr", {}))
|
||||
m = re.search(r"호칭압력 ([\d.]+)(㎏/㎠)를 기준", s)
|
||||
if m:
|
||||
out.append(("호칭압력 기준", R(m.group(1)), m.group(2), {}))
|
||||
m = re.search(r"강관장 ([\d.]+)m를 기준", s)
|
||||
if m:
|
||||
out.append(("강관장 기준", R(m.group(1)), "m", {}))
|
||||
return out
|
||||
|
||||
|
||||
def lift(chdir, text, out):
|
||||
src_tabs = list(out.tables)
|
||||
for t in src_tabs:
|
||||
ident = t["열쇠"].split()[0]
|
||||
tags = {}
|
||||
for line in t["주"]:
|
||||
rs = rows_of(line)
|
||||
if not rs:
|
||||
continue
|
||||
tag = "비고" if line.startswith("비고") else "주" + re.match(r"[①-⑳]", line).group(0)
|
||||
tags.setdefault(tag, []).extend(rs)
|
||||
for tag, rs in tags.items():
|
||||
cols, rows = {"값": "구분 따름", "단위": "글"}, []
|
||||
for g, v, u, extra in rs:
|
||||
rows.append({"구분": g, "값": v, "단위": u, **extra})
|
||||
for k in extra:
|
||||
cols.setdefault(k, "글")
|
||||
label = f"{t['이름']} 계수 {tag}"
|
||||
key = f"{ident} {label}"
|
||||
if key in out.keys:
|
||||
continue
|
||||
out.keys.add(key)
|
||||
out.tables.append(
|
||||
{
|
||||
"열쇠": key,
|
||||
"이름": label,
|
||||
"기준": "",
|
||||
"출처": f"{t['출처']} {tag}",
|
||||
"그룹": "계수",
|
||||
"조건": conds_out(["구분"], rows),
|
||||
"값칸": cols,
|
||||
"줄": rows,
|
||||
"주": [],
|
||||
}
|
||||
)
|
||||
@@ -0,0 +1,204 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""토목 표 손 지정 — 칸 안에 줄이 여럿(<br>)이거나 줄마다 머리가 달라 자동으로 못 읽는 표. (build_소요량_건설품셈_토목.py 가 씀)"""
|
||||
|
||||
import re
|
||||
|
||||
from _md표_읽기 import N, R, join
|
||||
from _토목_읽기 import is_num, nz
|
||||
from _토목_표 import bigo_items, conds_out, crew, jn, matrix, members, rate, tpose
|
||||
|
||||
SPECIAL = {"1": {}, "2": {}, "6": {}}
|
||||
|
||||
|
||||
def finish(rows, cols, bigo=(), label=None):
|
||||
names = []
|
||||
for r in rows:
|
||||
for k in r:
|
||||
if k not in names and k not in cols:
|
||||
names.append(k)
|
||||
return conds_out(names, rows), cols, rows, list(bigo), label
|
||||
|
||||
|
||||
def sp(ch, key):
|
||||
def deco(fn):
|
||||
SPECIAL[ch][key] = fn
|
||||
return fn
|
||||
|
||||
return deco
|
||||
|
||||
|
||||
def lines(x):
|
||||
return [p.strip() for p in x.split("<br>")]
|
||||
|
||||
|
||||
# ── 제1장 ──────────────────────────────────────────────────────
|
||||
sp("1", "1-8-9#4")(lambda t: (*tpose(t, "트럭", "작업"), None))
|
||||
sp("1", "1-8-2#2")(lambda t: (*tpose(t, "형식", "크레인"), None))
|
||||
sp("1", "1-8-14#2")(lambda t: (*rate(t), None))
|
||||
|
||||
|
||||
@sp("1", "1-9-1#1")
|
||||
def _(t):
|
||||
R0, rows, cols = t.rows, [], {"시공량": "개소"}
|
||||
heads = R0[2][5:8]
|
||||
for lo, hi in ((3, 6), (6, 9)):
|
||||
blk = R0[lo:hi]
|
||||
c_, v = members(blk, 2, 1, 3)
|
||||
cols.update(c_)
|
||||
hts = lines(blk[0][4])
|
||||
for j, hd in enumerate(heads):
|
||||
vs = lines(blk[0][5 + j])
|
||||
for i, hv in enumerate(hts):
|
||||
if nz(vs[i]):
|
||||
rows.append({"지주높이": hv, "지주 간격": hd, "시공량": N(vs[i]), **v})
|
||||
return finish(rows, cols)
|
||||
|
||||
|
||||
@sp("1", "1-9-1#2")
|
||||
def _(t):
|
||||
R0, rows, cols = t.rows, [], {"시공량": "개"}
|
||||
heads = [jn(x) for x in R0[2][5:9]]
|
||||
for lo, hi, hrows in ((3, 6, [3]), (6, 10, [6, 7, 8, 9])):
|
||||
c_, v = members(R0[lo:hi], 2, 1, 3)
|
||||
cols.update(c_)
|
||||
for r in hrows:
|
||||
for j, hd in enumerate(heads):
|
||||
if nz(R0[r][5 + j]):
|
||||
rows.append(
|
||||
{
|
||||
"지주높이": jn(R0[r][4]),
|
||||
"방음벽 개당 면적": hd,
|
||||
"시공량": N(R0[r][5 + j]),
|
||||
**v,
|
||||
}
|
||||
)
|
||||
return finish(rows, cols)
|
||||
|
||||
|
||||
@sp("1", "1-9-2#0")
|
||||
def _(t):
|
||||
R0, rows, cols = t.rows, [], {"시공량": "m"}
|
||||
for lo, hi in ((2, 5), (5, 8)):
|
||||
blk = R0[lo:hi]
|
||||
c_, v = members(blk, 3, 2, 4, 1)
|
||||
cols.update(c_)
|
||||
sizes = lines(blk[0][5])
|
||||
for j, kind in enumerate((R0[1][6], R0[1][7])):
|
||||
vs = lines(blk[0][6 + j])
|
||||
for i, sz in enumerate(sizes):
|
||||
rows.append(
|
||||
{
|
||||
"형식": jn(blk[0][0]),
|
||||
"규격(아래폭+높이㎜)": sz,
|
||||
"구간": kind,
|
||||
"시공량": N(vs[i]),
|
||||
**v,
|
||||
}
|
||||
)
|
||||
return finish(rows, cols)
|
||||
|
||||
|
||||
@sp("1", "1-9-4#2")
|
||||
def _(t):
|
||||
R0, rows, notes = t.rows, [], []
|
||||
for row in R0[1:]:
|
||||
if join(row[0]) == "산출기준":
|
||||
notes = ["산출기준 " + x[len("비고 ") :] for x in bigo_items(row[1])]
|
||||
continue
|
||||
d = {"구분": jn(row[0]), "단위": row[1], "수량": N(row[2])}
|
||||
if nz(row[3]):
|
||||
d["비고"] = jn(row[3])
|
||||
rows.append(d)
|
||||
return conds_out(["구분"], rows), {"수량": "단위 칸 따름", "비고": "글"}, rows, notes, None
|
||||
|
||||
|
||||
# ── 제6장 ──────────────────────────────────────────────────────
|
||||
@sp("6", "6-1-1#0")
|
||||
def _(t):
|
||||
rows = []
|
||||
for row in t.rows[2:]:
|
||||
d = {"구분": jn(row[0]), "내용": jn(row[1])}
|
||||
for nm, c in (("요율 품", 2), ("요율 시공량", 3)):
|
||||
if nz(row[c]):
|
||||
d[nm] = R(row[c].rstrip("%"))
|
||||
rows.append(d)
|
||||
return (
|
||||
conds_out(["구분"], rows),
|
||||
{"내용": "글", "요율 품": "%", "요율 시공량": "%"},
|
||||
rows,
|
||||
[],
|
||||
None,
|
||||
)
|
||||
|
||||
|
||||
def multi(t):
|
||||
"""칸 안에 관경 · 시공량이 줄줄이 든 [구분 단위 수량 관경 시공량] 표."""
|
||||
R0, rows, cols, bigo = t.rows, [], {}, []
|
||||
unit = re.search(r"\((.*)\)", join(R0[0][4])).group(1)
|
||||
cols["시공량"] = unit
|
||||
blocks = []
|
||||
for row in R0[1:]:
|
||||
if join(row[0]) == "비고":
|
||||
bigo += bigo_items(row[1])
|
||||
elif nz(row[3]):
|
||||
blocks.append([row])
|
||||
else:
|
||||
blocks[-1].append(row)
|
||||
for blk in blocks:
|
||||
c_, v = members(blk, 1, None, 2)
|
||||
cols.update(c_)
|
||||
ds, vs = lines(blk[0][3]), lines(blk[0][4])
|
||||
assert len(ds) == len(vs), (t.ident, ds, vs)
|
||||
for d, x in zip(ds, vs):
|
||||
rows.append({"관경(㎜)": d, "시공량": N(x), **v})
|
||||
return conds_out(["관경(㎜)"], rows), cols, rows, bigo, None
|
||||
|
||||
|
||||
for k in (
|
||||
"6-2-1#0",
|
||||
"6-2-1#1",
|
||||
"6-2-2#0",
|
||||
"6-2-2#1",
|
||||
"6-3-1#0",
|
||||
"6-3-1#1",
|
||||
"6-4-1#0",
|
||||
"6-4-2#0",
|
||||
"6-5-1#0",
|
||||
"6-5-2#0",
|
||||
"6-5-3#0",
|
||||
"6-5-4#0",
|
||||
"6-6-1#0",
|
||||
"6-6-2#0",
|
||||
):
|
||||
SPECIAL["6"][k] = multi
|
||||
sp("6", "6-2-2#2")(lambda t: (*rate(t), None))
|
||||
sp("6", "6-5-4#1")(lambda t: (*rate(t), None))
|
||||
sp("6", "6-8-5#2")(lambda t: (*rate(t), None))
|
||||
sp("6", "6-8-4#2")(lambda t: (*matrix(t, "본관", "지관", "중량", "㎏", 2), None))
|
||||
sp("6", "6-7-5#0")(lambda t: (*crew(t, prefix_group=True), None))
|
||||
|
||||
|
||||
@sp("6", "6-3-2#0")
|
||||
def _(t):
|
||||
R0, rows, cols = t.rows, [], {"시공량": "개소", "용접공": "인"}
|
||||
heads = [
|
||||
(jn(R0[0][4]), jn(R0[1][4]), jn(R0[2][4])),
|
||||
(None, jn(R0[1][4]), jn(R0[2][5])),
|
||||
(None, jn(R0[1][6]), jn(R0[2][6])),
|
||||
]
|
||||
combos = [(4, "A종", "겹치기 용접"), (5, "A종", "베벨엔드용접"), (6, "B종", "겹치기 용접")]
|
||||
for r in R0[3:]:
|
||||
if nz(r[0]):
|
||||
qty = N(r[2])
|
||||
for c, a, b in combos:
|
||||
if nz(r[c]):
|
||||
rows.append(
|
||||
{
|
||||
"관경(㎜)": jn(r[3]),
|
||||
"종별": a,
|
||||
"용접방법": b,
|
||||
"시공량": N(r[c]),
|
||||
"용접공": qty,
|
||||
}
|
||||
)
|
||||
return conds_out(["관경(㎜)", "종별", "용접방법"], rows), cols, rows, [], None
|
||||
@@ -0,0 +1,194 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""건설품셈 토목 제1 · 2 · 6장 md 읽기 · 표 만들기 · 쓰기 도구 (build_소요량_건설품셈_토목.py 가 씀).
|
||||
|
||||
값은 md 에서 읽음(손으로 적지 않음). 표 모양 다섯 갈래: 인력·장비 편성표(crew) · 줄이 관경인 납작표(flat) ·
|
||||
요율 · 글 표 · 손 지정.
|
||||
"""
|
||||
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
from _md표_읽기 import N, R, Raw, join, js, unesc
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
BASE = ROOT / "resources/knowledge/original/원가계산/건설공사_표준품셈/본문/02_토목부문"
|
||||
DIV = "건설품셈 토목 "
|
||||
NUM = r"\d[\d,]*(?:\.\d+)?"
|
||||
UNITS = "인 hr 시간 ㎜ ㎡ ㎥ m % 대 회 식 일 본 개소 개 ㎏ kg ton 톤 m/일".split()
|
||||
ROLE = re.compile(r"^(수량|시공량|소요일수)")
|
||||
|
||||
|
||||
class Tab:
|
||||
def __init__(self, ident, title, mok, base, rows, k):
|
||||
self.ident, self.title, self.mok, self.base, self.rows, self.k = (
|
||||
ident,
|
||||
title,
|
||||
mok,
|
||||
base,
|
||||
rows,
|
||||
k,
|
||||
)
|
||||
self.notes: list[str] = []
|
||||
self.pre = "" # 표 앞 글줄(별표 제목 등)
|
||||
|
||||
|
||||
def cells(line):
|
||||
return [c.strip() for c in line.strip().strip("|").split("|")]
|
||||
|
||||
|
||||
def title_of(s):
|
||||
return re.sub(r"\s*\((?:['‘’]\d\d[^)]*)\)\s*$", "", s).strip()
|
||||
|
||||
|
||||
def read_chapter(chdir):
|
||||
"""장 폴더의 항 · 표 · 주 를 차례로 읽음 → (표 목록, 항 본문 줄 사전)."""
|
||||
tabs, text, order = [], {}, []
|
||||
for p in sorted((next(BASE.glob(chdir + "*"))).glob("*.md")):
|
||||
L = p.read_text(encoding="utf-8").split("\n")
|
||||
at = [i for i, x in enumerate(L) if x.strip() == "---"]
|
||||
L = L[at[1] + 1 :]
|
||||
ident = title = mok = base = pre = ""
|
||||
k = 0
|
||||
in_note = False
|
||||
i = 0
|
||||
while i < len(L):
|
||||
ln = L[i].rstrip()
|
||||
m = re.match(r"^(#{2,4})\s+(.*)$", ln)
|
||||
if m:
|
||||
in_note = False
|
||||
d = len(m.group(1))
|
||||
if d == 3:
|
||||
mm = re.match(r"^(\d+-\d+-\d+)\s+(.*)$", m.group(2))
|
||||
ident, title, mok, k = mm.group(1), title_of(mm.group(2)), "", 0
|
||||
order.append(ident)
|
||||
text[ident] = []
|
||||
elif d == 4:
|
||||
mk = re.match(r"^(\d+)\.\s*(.*)$", m.group(2))
|
||||
mok = title_of(mk.group(2)) if mk else title_of(m.group(2))
|
||||
i += 1
|
||||
continue
|
||||
if not ident:
|
||||
i += 1
|
||||
continue
|
||||
text[ident].append(ln)
|
||||
if ln.startswith("|"):
|
||||
j = i
|
||||
rows = []
|
||||
while j < len(L) and L[j].startswith("|"):
|
||||
c = cells(L[j])
|
||||
if not all(re.fullmatch(r":?-+:?", x) for x in c):
|
||||
rows.append(c)
|
||||
j += 1
|
||||
b = base if re.fullmatch(r"\(.*당\)|\(m/일\)|\(.*\)", base) else ""
|
||||
tabs.append(Tab(ident, title, mok, base[1:-1] if b else "", rows, k))
|
||||
tabs[-1].pre = pre
|
||||
k += 1
|
||||
base = pre = ""
|
||||
in_note = False
|
||||
i = j
|
||||
continue
|
||||
s = ln.strip()
|
||||
if s == "[주]":
|
||||
in_note = True
|
||||
elif in_note and s and not s.startswith(("<!--", "![")):
|
||||
if re.match(r"^[①-⑳]", s):
|
||||
(tabs[-1].notes if tabs and tabs[-1].ident == ident else []).append(unesc(s))
|
||||
elif tabs and tabs[-1].ident == ident and tabs[-1].notes:
|
||||
tabs[-1].notes[-1] += " " + unesc(s)
|
||||
elif re.fullmatch(r"\(.*\)", s) and not s.startswith("(주"):
|
||||
base = s
|
||||
elif s.startswith("<별표>"):
|
||||
pre = s
|
||||
i += 1
|
||||
return tabs, text, order
|
||||
|
||||
|
||||
def unit_split(h):
|
||||
"""머리 「배관공(수도)(인)」 → (이름, 단위)."""
|
||||
h = join(h)
|
||||
m = re.search(r"\(([^()]*)\)$", h)
|
||||
if m and m.group(1) in UNITS:
|
||||
return h[: m.start()].strip(), m.group(1)
|
||||
return h, ""
|
||||
|
||||
|
||||
def fill_right(row, start):
|
||||
out, prev = [], ""
|
||||
for c in row[start:]:
|
||||
prev = c if c else prev
|
||||
out.append(prev)
|
||||
return out
|
||||
|
||||
|
||||
def is_num(x):
|
||||
return bool(re.fullmatch(NUM, x.strip()))
|
||||
|
||||
|
||||
class Out:
|
||||
def __init__(self):
|
||||
self.tables, self.keys = [], set()
|
||||
|
||||
def add(
|
||||
self, t, label, cond, cols, rows, *, src=None, base=None, group=None, rule=None, notes=None
|
||||
):
|
||||
ident = t.ident if t else src
|
||||
key = f"{ident} {label}"
|
||||
assert key not in self.keys, key
|
||||
self.keys.add(key)
|
||||
d = {
|
||||
"열쇠": key,
|
||||
"이름": label,
|
||||
"기준": (t.base if t else "") if base is None else base,
|
||||
"출처": DIV + (src or ident),
|
||||
}
|
||||
if group:
|
||||
d["그룹"] = group
|
||||
d["조건"] = cond
|
||||
if rule:
|
||||
d["범위규칙"] = rule
|
||||
d["값칸"] = cols
|
||||
d["줄"] = rows
|
||||
d["주"] = list(t.notes if (t and notes is None) else (notes or []))
|
||||
self.tables.append(d)
|
||||
|
||||
def write(self, path):
|
||||
L = ["{", ' "그룹": "소요량",', ' "원문": "건설품셈",', ' "판": "2026",', ' "표": [']
|
||||
for ti, t in enumerate(self.tables):
|
||||
L.append(" {")
|
||||
keys = list(t)
|
||||
for ki, k in enumerate(keys):
|
||||
end = "," if ki < len(keys) - 1 else ""
|
||||
v = t[k]
|
||||
if k in ("줄", "주") and v:
|
||||
L.append(f" {js(k)}: [")
|
||||
L += [
|
||||
f" {js(x)}" + ("," if xi < len(v) - 1 else "")
|
||||
for xi, x in enumerate(v)
|
||||
]
|
||||
L.append(f" ]{end}")
|
||||
else:
|
||||
L.append(f" {js(k)}: {js(v)}{end}")
|
||||
L.append(" }" + ("," if ti < len(self.tables) - 1 else ""))
|
||||
L += [" ]", "}", ""]
|
||||
Path(path).write_text("\n".join(L), encoding="utf-8", newline="\n")
|
||||
|
||||
|
||||
def kind_of(vals):
|
||||
"""조건 칸 종류 — 모두 수면 수 · 아니면 고르기."""
|
||||
return "수" if all(is_num(v) for v in vals) else "고르기"
|
||||
|
||||
|
||||
def nz(x):
|
||||
return x not in ("", "-")
|
||||
|
||||
|
||||
def cell_val(x):
|
||||
"""값 칸 하나 → (값, 원문 or None). 수 하나가 든 글 = 수 + 원문 · 그 밖 글은 글."""
|
||||
x = join(x)
|
||||
if is_num(x):
|
||||
return N(x), None
|
||||
nums = re.findall(NUM, x)
|
||||
if len(nums) == 1 and re.search(r"[가-힣%a-zA-Z]", x) and not re.search(r"[∼~\s]", x):
|
||||
return R(nums[0]), x
|
||||
return x, None
|
||||
@@ -0,0 +1,322 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""토목 표 만들기 — 편성표(crew) · 납작표(flat) · 요율 · 매트릭스. (build_소요량_건설품셈_토목.py 가 씀)"""
|
||||
|
||||
import re
|
||||
|
||||
from _md표_읽기 import N, R, Raw, join
|
||||
from _토목_읽기 import NUM, ROLE, UNITS, cell_val, fill_right, is_num, kind_of, nz, unit_split
|
||||
|
||||
|
||||
def head_paths(rows, h, start, ncol):
|
||||
"""머리 h 줄 → 열별 층 목록. 빈 칸은 같은 맨 윗 층 무리 안에서만 왼쪽 값으로 채움."""
|
||||
top, paths = fill_right(rows[0][start:], 0), {c: [] for c in range(start, ncol)}
|
||||
for r in range(h):
|
||||
row = list(rows[r][start:])
|
||||
for i in range(1, len(row)):
|
||||
if not row[i] and r > 0 and top[i] == top[i - 1]:
|
||||
row[i] = row[i - 1]
|
||||
elif not row[i] and r == 0:
|
||||
row[i] = row[i - 1]
|
||||
for i, c in enumerate(range(start, ncol)):
|
||||
v = jn(row[i]) if row[i] else ""
|
||||
if v and (not paths[c] or paths[c][-1] != v):
|
||||
paths[c].append(v)
|
||||
return paths
|
||||
|
||||
|
||||
def is_text(v):
|
||||
return isinstance(v, str) and not isinstance(v, Raw)
|
||||
|
||||
|
||||
def jn(cell):
|
||||
"""칸 글 — 「∼」 둘레 줄바꿈은 붙임."""
|
||||
if "∼" in cell:
|
||||
return re.sub(r"\s*<br>\s*", "", cell).replace(" ∼", "∼").replace("∼ ", "∼")
|
||||
return join(cell)
|
||||
|
||||
|
||||
def is_unit(p):
|
||||
return p in UNITS or bool(re.fullmatch(r"[㎡㎥㎜㎝㎏a-zA-Z/개소본일회]+", p))
|
||||
|
||||
|
||||
def conds_out(names, rows):
|
||||
"""조건 이름별 종류를 정해 줄의 글을 수로 바꿈 → 조건 사전."""
|
||||
cond = {}
|
||||
for nm in names:
|
||||
vals = [r[nm] for r in rows if nm in r]
|
||||
kd = kind_of(vals)
|
||||
cond[nm] = kd
|
||||
if kd == "수":
|
||||
for r in rows:
|
||||
if nm in r:
|
||||
r[nm] = N(r[nm])
|
||||
return cond
|
||||
|
||||
|
||||
def bigo_items(text):
|
||||
items = []
|
||||
for p in (x.strip() for x in text.split("<br>")):
|
||||
if p.startswith("- ") or not items:
|
||||
items.append(p)
|
||||
else:
|
||||
items[-1] += " " + p
|
||||
return ["비고 " + x for x in items]
|
||||
|
||||
|
||||
def split_name(nm, spec):
|
||||
"""이름 끝 괄호에 수가 들었으면(「…(폭 2.4m)」) 이름에서 떼어 규격 글로."""
|
||||
m = re.search(r"\(([^()]*\d[^()]*)\)$", nm)
|
||||
if m and " " not in nm[: m.start()].strip():
|
||||
return nm[: m.start()].strip(), (m.group(1) + (" " + spec if spec else ""))
|
||||
return nm, spec
|
||||
|
||||
|
||||
# ── 편성표 ─────────────────────────────────────────────────────
|
||||
def crew(t, cn=(), gname=None, prefix_group=False):
|
||||
R0 = t.rows
|
||||
head0 = R0[0]
|
||||
uc = next(i for i, x in enumerate(head0) if join(x) == "단위")
|
||||
sc = next((i for i, x in enumerate(head0) if join(x).startswith("규격")), None)
|
||||
ncol = len(head0)
|
||||
h = next(i for i in range(1, len(R0)) if R0[i][uc] != "")
|
||||
namecols = [c for c in range(uc) if c != sc]
|
||||
paths = head_paths(R0, h, uc + 1, ncol)
|
||||
role, sig, cname_of = {}, {}, {}
|
||||
for c, pth in paths.items():
|
||||
ri = next((i for i, x in enumerate(pth) if ROLE.match(x)), None)
|
||||
if ri is None: # 역할 글 없음(머리 = 조건) — 값은 모두 수량
|
||||
role[c] = ("수량", "")
|
||||
sig[c] = tuple(pth[1:] if len(pth) > 1 else pth)
|
||||
cname_of[c] = (
|
||||
[
|
||||
re.sub(r"\(.*\)$", "", pth[0]).strip()
|
||||
+ (
|
||||
re.search(r"\(.*\)$", pth[0]).group(0)
|
||||
if re.search(r"\(.*\)$", pth[0])
|
||||
else ""
|
||||
)
|
||||
]
|
||||
if len(pth) > 1
|
||||
else []
|
||||
)
|
||||
continue
|
||||
m = ROLE.match(pth[ri])
|
||||
par = re.search(r"\((.*)\)", pth[ri])
|
||||
p = par.group(1) if par else ""
|
||||
role[c] = (m.group(1), p)
|
||||
rest = [x for i, x in enumerate(pth) if i != ri]
|
||||
sig[c] = tuple(rest)
|
||||
cname_of[c] = [p] if (p and not is_unit(p) and rest) else []
|
||||
prod = [c for c in role if role[c][0] != "수량"]
|
||||
nc = namecols[-1]
|
||||
for r in range(h, len(R0)): # 이름 칸이 비면(같은 이름 잇따름) 위 이름
|
||||
if not R0[r][nc] and R0[r][uc] and R0[r - 1][nc]:
|
||||
R0[r][nc] = R0[r - 1][nc]
|
||||
# 블록
|
||||
blocks, bigo, grp = [], [], ""
|
||||
for r in range(h, len(R0)):
|
||||
row = R0[r]
|
||||
if join(row[0]) == "비고":
|
||||
bigo += bigo_items(row[1])
|
||||
continue
|
||||
if len(namecols) > 1 and row[namecols[0]]:
|
||||
grp = join(row[namecols[0]])
|
||||
if (prod and any(row[c] for c in prod)) or not blocks:
|
||||
blocks.append({"grp": grp, "rows": []})
|
||||
blocks[-1]["rows"].append((grp, row))
|
||||
cols, rows, condnames = {}, [], []
|
||||
gcol = gname or (join(head0[namecols[0]]) if len(namecols) > 1 else "")
|
||||
sigs = []
|
||||
for c in sorted(role):
|
||||
if sig[c] not in sigs:
|
||||
sigs.append(sig[c])
|
||||
nonempty = [s for s in sigs if s]
|
||||
outsigs = nonempty if nonempty else [()]
|
||||
for blk in blocks:
|
||||
first = blk["rows"][0][1]
|
||||
for sg in outsigs:
|
||||
row = {}
|
||||
if gcol and prod:
|
||||
row[gcol] = blk["grp"]
|
||||
names = list(cn) or next(
|
||||
(cname_of[c] for c in role if sig[c] == sg and cname_of[c]), []
|
||||
)
|
||||
for lv, val in enumerate(sg):
|
||||
nm = names[lv] if lv < len(names) else f"조건{lv + 1}"
|
||||
if nm == "-":
|
||||
continue
|
||||
row[nm] = val[len(nm) + 1 :] if val.startswith(nm + " ") else val
|
||||
vcols = [c for c in role if sig[c] in (sg, ())]
|
||||
have_prod = False
|
||||
for c in vcols:
|
||||
if role[c][0] != "수량" and nz(first[c]):
|
||||
nm = role[c][0]
|
||||
pu = role[c][1] if is_unit(role[c][1]) else ""
|
||||
cols[nm] = pu
|
||||
row[nm] = N(first[c])
|
||||
have_prod = True
|
||||
if prod and not have_prod:
|
||||
continue
|
||||
for g, mrow in blk["rows"]:
|
||||
nm = jn(mrow[namecols[-1]])
|
||||
if prefix_group and g:
|
||||
nm = f"{g} {nm}"
|
||||
unit = mrow[uc]
|
||||
for c in vcols:
|
||||
if role[c][0] == "수량" and nz(mrow[c]):
|
||||
assert is_num(mrow[c]), (t.ident, mrow[c])
|
||||
spec = jn(mrow[sc]) if sc is not None and nz(mrow[sc]) else ""
|
||||
nm, spec = split_name(nm, spec)
|
||||
if nm in row: # 같은 이름 둘(규격만 다름) — 이름 뒤에 규격을 붙여 가름
|
||||
nm = f"{nm} {spec}"
|
||||
cols.setdefault(nm, unit)
|
||||
row[nm] = N(mrow[c])
|
||||
if spec and " " + spec not in nm:
|
||||
cols[nm + "규격"] = "글"
|
||||
row[nm + "규격"] = spec
|
||||
rows.append(row)
|
||||
cnames = []
|
||||
for r in rows:
|
||||
for k in r:
|
||||
if k not in cnames and k not in cols:
|
||||
cnames.append(k)
|
||||
cond = conds_out(cnames, rows)
|
||||
return cond, cols, rows, bigo
|
||||
|
||||
|
||||
# ── 납작표 ─────────────────────────────────────────────────────
|
||||
def flat(t, lead=1, levels=("V",), hdr=None, vname=None, halves=False, unit=None):
|
||||
R0 = t.rows
|
||||
bigo = []
|
||||
body_rows = []
|
||||
for row in R0:
|
||||
if join(row[0]) == "비고":
|
||||
bigo += bigo_items(row[1])
|
||||
R0 = [r for r in R0 if join(r[0]) != "비고"]
|
||||
if halves:
|
||||
w = len(R0[0]) // 2
|
||||
parts = [[r[:w] for r in R0], [r[w:] for r in R0]]
|
||||
else:
|
||||
parts = [R0]
|
||||
cols, rows, condnames = {}, [], []
|
||||
for P in parts:
|
||||
h = hdr
|
||||
if h is None:
|
||||
h = next(i for i, row in enumerate(P) if any(is_num(c) for c in row[lead:]))
|
||||
ncol = len(P[0])
|
||||
lead_names = []
|
||||
for c in range(lead):
|
||||
hs = [jn(P[r][c]) for r in range(h) if P[r][c]]
|
||||
lead_names.append(re.sub(r"\s+\(", "(", hs[-1]) if hs else f"조건{c + 1}")
|
||||
paths = head_paths(P, h, lead, ncol)
|
||||
info = {}
|
||||
for c, pth in paths.items():
|
||||
cn_, vn_ = {}, []
|
||||
for i, el in enumerate(pth):
|
||||
lv = levels[i] if i < len(levels) else "V"
|
||||
if lv.startswith("C:"):
|
||||
cn_[lv[2:]] = el
|
||||
elif lv == "V":
|
||||
vn_.append(el)
|
||||
info[c] = (tuple(cn_.items()), " ".join(vn_))
|
||||
prev = [""] * lead
|
||||
for row in P[h:]:
|
||||
if not any(row):
|
||||
continue
|
||||
lc = {}
|
||||
for c in range(lead):
|
||||
if row[c]:
|
||||
prev[c] = jn(row[c])
|
||||
for c2 in range(c + 1, lead):
|
||||
prev[c2] = ""
|
||||
lc[lead_names[c]] = prev[c]
|
||||
sigs = []
|
||||
for c in range(lead, ncol):
|
||||
if info[c][0] not in sigs:
|
||||
sigs.append(info[c][0])
|
||||
for sg in sigs:
|
||||
out = dict(lc)
|
||||
for k, v in sg:
|
||||
out[k] = v
|
||||
got = False
|
||||
for c in range(lead, ncol):
|
||||
if info[c][0] != sg or not nz(row[c]):
|
||||
continue
|
||||
name, un = unit_split(info[c][1]) if info[c][1] else (vname, "")
|
||||
if info[c][1] == "" or vname:
|
||||
name = vname or name
|
||||
un = unit or t.base
|
||||
v, orig = cell_val(row[c])
|
||||
cols[name] = "글" if is_text(v) or cols.get(name) == "글" else un
|
||||
out[name] = v
|
||||
if orig:
|
||||
cols[name + "원문"] = "글"
|
||||
out[name + "원문"] = orig
|
||||
got = True
|
||||
if got:
|
||||
rows.append(out)
|
||||
names = []
|
||||
for r in rows:
|
||||
for k in r:
|
||||
if k not in names and k not in cols:
|
||||
names.append(k)
|
||||
cond = conds_out(names, rows)
|
||||
return cond, cols, rows, bigo
|
||||
|
||||
|
||||
def rate(t, cname=None, vname=None):
|
||||
"""머리 = 조건, 둘째 줄 = 요율(%)."""
|
||||
h, b = t.rows[0], t.rows[1]
|
||||
cname = cname or jn(h[0])
|
||||
vn = vname or re.sub(r"\(%\)$", "", jn(b[0]))
|
||||
rows = [{cname: jn(h[c]), vn: R(b[c].rstrip("%"))} for c in range(1, len(h)) if nz(b[c])]
|
||||
return {cname: "고르기"}, {vn: "%"}, rows, []
|
||||
|
||||
|
||||
def matrix(t, rname, cname, vname, unit, hdr):
|
||||
"""줄 머리 · 칸 머리 → 값 하나."""
|
||||
P = t.rows
|
||||
colh = [jn(x) for x in P[0]]
|
||||
rows = []
|
||||
for row in P[hdr:]:
|
||||
for c in range(1, len(row)):
|
||||
if nz(row[c]):
|
||||
rows.append({rname: jn(row[0]), cname: colh[c], vname: N(row[c])})
|
||||
cond = conds_out([rname, cname], rows)
|
||||
return cond, {vname: unit}, rows, []
|
||||
|
||||
|
||||
def tpose(t, cname, vname):
|
||||
"""머리 = 조건, 둘째 줄 = 값(수 하나가 든 글은 수 + 원문)."""
|
||||
h, b = t.rows[0], t.rows[1]
|
||||
rows, cols = [], {}
|
||||
for c in range(1, len(h)):
|
||||
if not nz(b[c]):
|
||||
continue
|
||||
v, orig = cell_val(b[c])
|
||||
d = {cname: jn(h[c]), vname: v}
|
||||
cols[vname] = "글" if is_text(v) else ""
|
||||
if orig:
|
||||
d[vname + "원문"] = orig
|
||||
cols[vname + "원문"] = "글"
|
||||
rows.append(d)
|
||||
return {cname: kind_of([r[cname] for r in rows])}, cols, rows, []
|
||||
|
||||
|
||||
def members(rows, uc, sc, qc, nc=0):
|
||||
"""몸 줄들 → (값칸, 값) — 수량 칸이 「-」 · 빈칸이면 뺌."""
|
||||
cols, vals = {}, {}
|
||||
for row in rows:
|
||||
if not nz(row[qc]):
|
||||
continue
|
||||
nm = jn(row[nc])
|
||||
spec = jn(row[sc]) if sc is not None and nz(row[sc]) else ""
|
||||
nm, spec = split_name(nm, spec)
|
||||
if nm in vals:
|
||||
nm = f"{nm} {spec}"
|
||||
cols.setdefault(nm, row[uc])
|
||||
vals[nm] = N(row[qc])
|
||||
if spec and " " + spec not in nm:
|
||||
cols[nm + "규격"] = "글"
|
||||
vals[nm + "규격"] = spec
|
||||
return cols, vals
|
||||
@@ -0,0 +1,165 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""건설품셈 토목 제1 · 2 · 6장 본문 md → resources/master_data/소요량_건설품셈_토목{1,2,6}장.json
|
||||
|
||||
값은 본문 md 에서만 읽음(손으로 적지 않음). 표마다 모양이 달라 표별 지정(CN · FLAT · 손 함수)과 글 속 계수(_토목_계수)는 손으로 둠.
|
||||
돌리기: ./venv/Scripts/python.exe resources/master_data/scripts/build_소요량_건설품셈_토목.py
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
from _md표_읽기 import N, R, join
|
||||
from _토목_계수 import lift
|
||||
from _토목_손 import SPECIAL
|
||||
from _토목_읽기 import ROOT, Out, read_chapter
|
||||
from _토목_표 import crew, flat, rate, tpose
|
||||
|
||||
# 편성표 — 시공량 머리 층 이름(「-」 = 이름만 있는 층이라 뺌)
|
||||
CN = {
|
||||
"1-1-2#0": ["설치간격"],
|
||||
"1-5-3#0": ["-", "1층 포설두께"],
|
||||
"1-5-4#0": ["시공폭", "-", "1층 포설두께"],
|
||||
"1-5-7#0": ["시공폭"],
|
||||
"1-5-8#0": ["시공폭"],
|
||||
"1-5-9#0": ["시공폭"],
|
||||
"1-6-1#0": ["포장"],
|
||||
"1-6-2#0": ["형식", "두께"],
|
||||
"1-6-3#0": ["포장"],
|
||||
"1-6-4#0": ["포장"],
|
||||
"1-6-5#0": ["작업"],
|
||||
"1-7-1#0": ["형식"],
|
||||
"1-7-2#0": ["형식"],
|
||||
"1-8-1#0": ["작업"],
|
||||
"1-8-2#0": ["형식"],
|
||||
"1-8-3#0": ["면"],
|
||||
"1-8-5#0": ["설치위치"],
|
||||
"1-8-9#0": ["도색"],
|
||||
"1-8-9#1": ["도색"],
|
||||
"1-8-9#2": ["도색"],
|
||||
"1-8-9#3": ["도색"],
|
||||
"1-8-9#6": ["도색"],
|
||||
"1-8-10#0": ["지주간격"],
|
||||
"1-8-10#1": ["지주간격", "W"],
|
||||
"1-8-11#0": ["지주간격"],
|
||||
"1-8-11#1": ["지주간격", "W"],
|
||||
"1-8-12#0": ["높이"],
|
||||
"1-8-13#0": ["형식"],
|
||||
"1-8-14#0": ["형식"],
|
||||
"1-8-15#0": ["높이"],
|
||||
"1-9-4#1": [],
|
||||
"2-2-1#0": ["돌망태 높이"],
|
||||
"2-3-1#0": ["작업"],
|
||||
"6-7-5#1": [],
|
||||
}
|
||||
# 납작표 — lead(줄 조건 칸 수) · levels(머리 층: C:이름 = 조건 · V = 값 이름 · N = 뺌) · hdr
|
||||
FLAT = {
|
||||
"1-6-2#1": dict(lead=1, hdr=1),
|
||||
"1-7-1#1": dict(lead=1, hdr=1),
|
||||
"1-7-2#1": dict(lead=1, hdr=1),
|
||||
"1-8-13#1": dict(lead=1, hdr=1),
|
||||
"1-8-14#1": dict(lead=1, hdr=1),
|
||||
"1-9-2#1": dict(lead=1, hdr=1),
|
||||
"6-2-3#0": dict(lead=1, hdr=1, halves=True),
|
||||
"6-3-3#0": dict(lead=1, hdr=2, levels=("C:도장부위", "V")),
|
||||
"6-3-4#0": dict(lead=1, hdr=2, halves=True, levels=("C:종별", "V")),
|
||||
"6-5-5#0": dict(lead=1, hdr=1),
|
||||
"6-6-3#0": dict(lead=1, hdr=1, halves=True),
|
||||
"6-6-4#0": dict(lead=2, hdr=2),
|
||||
"6-7-1#0": dict(lead=1, hdr=1),
|
||||
"6-7-1#1": dict(lead=1, hdr=1),
|
||||
"6-7-2#0": dict(lead=1, hdr=1),
|
||||
"6-7-3#0": dict(lead=1, hdr=2, levels=("V", "C:관 종류")),
|
||||
"6-7-3#1": dict(lead=1, hdr=1),
|
||||
"6-7-4#0": dict(lead=1, hdr=1),
|
||||
"6-7-5#3": dict(lead=1, hdr=3, levels=("C:토질", "-", "C:추진연장(m)"), vname="작업능력"),
|
||||
"6-8-1#0": dict(lead=1, hdr=1),
|
||||
"6-8-1#1": dict(lead=2, hdr=2, levels=("-", "V")),
|
||||
"6-8-1#2": dict(lead=1, hdr=1),
|
||||
"6-8-2#0": dict(lead=1, hdr=1),
|
||||
"6-8-2#1": dict(lead=1, hdr=1),
|
||||
"6-8-3#0": dict(lead=1, hdr=1),
|
||||
"6-8-3#1": dict(lead=1, hdr=1),
|
||||
"6-8-4#0": dict(lead=1, hdr=1),
|
||||
"6-8-4#1": dict(lead=1, hdr=1),
|
||||
"6-8-5#0": dict(lead=1, hdr=1),
|
||||
"6-8-5#1": dict(lead=1, hdr=1),
|
||||
"6-8-6#0": dict(lead=2, hdr=2),
|
||||
"6-8-7#0": dict(lead=1, hdr=2, levels=("V", "V")),
|
||||
}
|
||||
LABEL = { # "항#k": 표 이름(기본 = 항 제목 + 목/표 번호)
|
||||
"1-6-2#1": "표층 인력포설 적용기준",
|
||||
"1-7-1#1": "보도용 블록 설치(소형) 적용기준",
|
||||
"1-7-2#1": "보도용 블록 설치(대형) 적용기준",
|
||||
"1-8-2#1": "도로 표지판 설치 표지판 규격별",
|
||||
"1-8-2#2": "도로 표지판 설치 크레인 규격",
|
||||
"1-8-9#4": "융착식 도료 수동식 트럭 작업",
|
||||
"1-8-9#5": "융착식 도료 수동식 재료",
|
||||
"1-8-13#1": "유색포장(미끄럼방지) 적용기준",
|
||||
"1-8-14#1": "표시못 설치 적용기준",
|
||||
"1-8-14#2": "표시못 설치 요율",
|
||||
"1-9-2#1": "보차도 및 도로경계블록 설치 적용기준",
|
||||
"1-9-4#2": "낙석방지망 설치 재료",
|
||||
"6-2-1#1": "타이튼 접합 및 부설 인력",
|
||||
"6-2-2#1": "K.P 메커니컬 접합 및 부설 인력",
|
||||
"6-2-2#2": "K.P 메커니컬 접합 및 부설 요율",
|
||||
"6-3-1#1": "부설 인력",
|
||||
"6-5-4#1": "바트융착 접합 및 부설 요율",
|
||||
"6-7-1#1": "PC관 부설 및 접합 크레인 규격",
|
||||
"6-7-3#1": "유리섬유복합관 부설 및 접합 크레인 규격",
|
||||
"6-7-5#4": "강관압입추진공 조명시설",
|
||||
"6-8-1#1": "주철제 게이트 제수밸브 부설 및 접합 인력",
|
||||
"6-8-1#2": "주철제 게이트 제수밸브 부설 및 접합 크레인 규격",
|
||||
"6-8-2#1": "강관제 게이트 제수밸브 부설 및 접합 크레인 규격",
|
||||
"6-8-3#1": "주철제・강관제 버터플라이 제수밸브 부설 및 접합 크레인 규격",
|
||||
"6-8-4#1": "부단수 할정자관 부설 및 접합 부설장비 규격",
|
||||
"6-8-4#2": "부단수 할정자관 부설 및 접합 할정자관 중량표",
|
||||
"6-8-5#1": "부단수 천공 분기점 분기 부설장비 규격",
|
||||
"6-8-5#2": "부단수 천공 분기점 분기 요율",
|
||||
}
|
||||
|
||||
|
||||
def label_of(t, all_in_item):
|
||||
key = f"{t.ident}#{t.k}"
|
||||
if key in LABEL:
|
||||
return LABEL[key]
|
||||
if t.mok:
|
||||
return f"{t.title} {t.mok}"
|
||||
return t.title if all_in_item[t.ident] == 1 or t.k == 0 else f"{t.title} 표{t.k + 1}"
|
||||
|
||||
|
||||
def build(chdir, out_name, hand):
|
||||
tabs, text, order = read_chapter(chdir)
|
||||
count = {}
|
||||
for t in tabs:
|
||||
count[t.ident] = count.get(t.ident, 0) + 1
|
||||
out = Out()
|
||||
for t in tabs:
|
||||
key = f"{t.ident}#{t.k}"
|
||||
h0 = [join(x) for x in t.rows[0]]
|
||||
label = label_of(t, count)
|
||||
if key in hand:
|
||||
cond, cols, rows, bigo, lab = hand[key](t)
|
||||
label = lab or label
|
||||
rule = None
|
||||
elif key in FLAT:
|
||||
cond, cols, rows, bigo = flat(t, **FLAT[key])
|
||||
elif "단위" in h0:
|
||||
cond, cols, rows, bigo = crew(t, CN.get(key, ()))
|
||||
else:
|
||||
raise SystemExit(f"지정 없는 표: {key} {h0[:4]}")
|
||||
out.add(t, label, cond, cols, rows, notes=bigo + t.notes)
|
||||
lift(chdir, text, out)
|
||||
out.write(ROOT / "resources/master_data" / out_name)
|
||||
return out
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
which = sys.argv[1:] or ["1", "2", "6"]
|
||||
for w in which:
|
||||
ch = {
|
||||
"1": ("제01장", "소요량_건설품셈_토목1장.json"),
|
||||
"2": ("제02장", "소요량_건설품셈_토목2장.json"),
|
||||
"6": ("제06장", "소요량_건설품셈_토목6장.json"),
|
||||
}[w]
|
||||
o = build(ch[0], ch[1], SPECIAL.get(w, {}))
|
||||
print(w, len(o.tables), "표")
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,155 @@
|
||||
{
|
||||
"그룹": "소요량",
|
||||
"원문": "건설품셈",
|
||||
"판": "2026",
|
||||
"표": [
|
||||
{
|
||||
"열쇠": "2-1-1 사석부설",
|
||||
"이름": "사석부설",
|
||||
"기준": "㎥당",
|
||||
"출처": "건설품셈 토목 2-1-1",
|
||||
"조건": {},
|
||||
"값칸": { "보통인부": "인", "굴삭기": "hr", "굴삭기규격": "글" },
|
||||
"줄": [
|
||||
{ "보통인부": 0.004, "굴삭기": 0.027, "굴삭기규격": "1.0㎥" }
|
||||
],
|
||||
"주": [
|
||||
"① 본 품은 굴착기를 사용하여 사석을 부설하는 기준이다.",
|
||||
"② 본 품은 사석 부설 및 정리 작업이 포함된 것이다.",
|
||||
"③ 필터매트 설치는 ‘[공통부문] 5-2-1 매트부설’을 따른다."
|
||||
]
|
||||
},
|
||||
{
|
||||
"열쇠": "2-1-2 사석고르기",
|
||||
"이름": "사석고르기",
|
||||
"기준": "㎡당",
|
||||
"출처": "건설품셈 토목 2-1-2",
|
||||
"조건": {},
|
||||
"값칸": { "보통인부": "인", "굴삭기+부착용집게": "hr", "굴삭기+부착용집게규격": "글" },
|
||||
"줄": [
|
||||
{ "보통인부": 0.005, "굴삭기+부착용집게": 0.070, "굴삭기+부착용집게규격": "1.0㎥" }
|
||||
],
|
||||
"주": [
|
||||
"① 본 품은 사석 부설 후 굴착기(집게)를 사용하여 표면부 사석을 돌출되지 않게 고르는 기준이다.",
|
||||
"② 사석 고르기, 잡석 채움 작업을 포함한다."
|
||||
]
|
||||
},
|
||||
{
|
||||
"열쇠": "2-2-1 타원형 돌망태 설치",
|
||||
"이름": "타원형 돌망태 설치",
|
||||
"기준": "㎡당",
|
||||
"출처": "건설품셈 토목 2-2-1",
|
||||
"조건": { "돌망태 높이": "고르기" },
|
||||
"값칸": { "석공": "인", "특별인부": "인", "보통인부": "인", "굴착기": "hr", "굴착기규격": "글" },
|
||||
"줄": [
|
||||
{ "돌망태 높이": "40㎝", "석공": 0.039, "특별인부": 0.013, "보통인부": 0.005, "굴착기": 0.026, "굴착기규격": "1.0㎥" },
|
||||
{ "돌망태 높이": "45㎝", "석공": 0.044, "특별인부": 0.014, "보통인부": 0.006, "굴착기": 0.030, "굴착기규격": "1.0㎥" },
|
||||
{ "돌망태 높이": "50㎝", "석공": 0.049, "특별인부": 0.016, "보통인부": 0.007, "굴착기": 0.033, "굴착기규격": "1.0㎥" },
|
||||
{ "돌망태 높이": "60㎝", "석공": 0.063, "특별인부": 0.019, "보통인부": 0.008, "굴착기": 0.040, "굴착기규격": "1.0㎥" },
|
||||
{ "돌망태 높이": "70㎝", "석공": 0.073, "특별인부": 0.024, "보통인부": 0.010, "굴착기": 0.046, "굴착기규격": "1.0㎥" }
|
||||
],
|
||||
"주": [
|
||||
"① 본 품은 타원형 돌망태를 설치하는 기준이다.",
|
||||
"② 망태석 포설, 망태 조립 및 설치, 망태석 채움, 망태조임 및 마무리 작업을 포함한다.",
|
||||
"③ 필터매트를 설치할 경우 ‘[공통부문] 5-2-1 매트부설’을 따른다."
|
||||
]
|
||||
},
|
||||
{
|
||||
"열쇠": "2-2-2 매트리스형 돌망태 설치",
|
||||
"이름": "매트리스형 돌망태 설치",
|
||||
"기준": "㎡당",
|
||||
"출처": "건설품셈 토목 2-2-2",
|
||||
"조건": {},
|
||||
"값칸": { "석공": "인", "특별인부": "인", "보통인부": "인", "굴착기": "hr", "굴착기규격": "글" },
|
||||
"줄": [
|
||||
{ "석공": 0.027, "특별인부": 0.010, "보통인부": 0.010, "굴착기": 0.025, "굴착기규격": "1.0㎥" }
|
||||
],
|
||||
"주": [
|
||||
"① 본 품은 매트리스형 돌망태(폭 200㎝, 높이 30㎝)를 설치하는 기준이다.",
|
||||
"② 본 품은 망태 조립 및 설치, 망태석 채움, 덮개 조립 작업이 포함된 것이다.",
|
||||
"③ 필터매트 설치는 ‘[공통부문] 5-2-1 매트부설’을 따른다."
|
||||
]
|
||||
},
|
||||
{
|
||||
"열쇠": "2-2-3 돌망태형옹벽 설치",
|
||||
"이름": "돌망태형옹벽 설치",
|
||||
"기준": "㎥당",
|
||||
"출처": "건설품셈 토목 2-2-3",
|
||||
"조건": {},
|
||||
"값칸": { "석공": "인", "특별인부": "인", "보통인부": "인", "굴착기": "hr", "굴착기규격": "글" },
|
||||
"줄": [
|
||||
{ "석공": 0.190, "특별인부": 0.134, "보통인부": 0.117, "굴착기": 0.281, "굴착기규격": "0.6㎥" }
|
||||
],
|
||||
"주": [
|
||||
"① 본 품은 높이 5m이하의 돌망태옹벽(GABION 철망태)을 설치하는 기준이다.",
|
||||
"② 철망태의 조립 및 설치, 망태석 채움, 덮개조립 작업을 포함한다.",
|
||||
"③ 터파기 및 지반고르기는 별도 계상한다.",
|
||||
"④ 필터매트를 설치할 경우 ‘[공통부문] 5-2-1 매트부설’을 따른다."
|
||||
]
|
||||
},
|
||||
{
|
||||
"열쇠": "2-3-1 식생매트 설치",
|
||||
"이름": "식생매트 설치",
|
||||
"기준": "㎡당",
|
||||
"출처": "건설품셈 토목 2-3-1",
|
||||
"조건": { "작업": "고르기" },
|
||||
"값칸": { "특별인부": "인", "보통인부": "인", "굴삭기": "hr", "굴삭기규격": "글" },
|
||||
"줄": [
|
||||
{ "작업": "식생매트설치", "특별인부": 0.014, "보통인부": 0.003 },
|
||||
{ "작업": "복토", "보통인부": 0.005, "굴삭기": 0.031, "굴삭기규격": "0.6㎥" }
|
||||
],
|
||||
"주": [
|
||||
"① 본 품은 호안등사면에 식생매트를 설치하는 기준이다.",
|
||||
"② 인력 흙고르기, 식생매트 깔기, 복토 작업을 포함한다.",
|
||||
"③ 매트부설 이외 기타공종(종자살포, 잔디심기, 관수, 시비 등)는 별도 계상한다."
|
||||
]
|
||||
},
|
||||
{
|
||||
"열쇠": "2-3-2 블록 붙이기(인력)",
|
||||
"이름": "블록 붙이기(인력)",
|
||||
"기준": "㎡당",
|
||||
"출처": "건설품셈 토목 2-3-2",
|
||||
"조건": {},
|
||||
"값칸": { "특별인부": "인", "보통인부": "인" },
|
||||
"줄": [
|
||||
{ "특별인부": 0.076, "보통인부": 0.066 }
|
||||
],
|
||||
"주": [
|
||||
"① 본 품은 하천제방에 인력으로 호안블록을 설치하는 기준이다.",
|
||||
"② 본 품은 호안블록 설치, 철물 연결 작업이 포함된 것이다.",
|
||||
"③ 비탈면 고르기, 흙 채움 및 잔디심기가 필요한 경우에는 별도 계상한다."
|
||||
]
|
||||
},
|
||||
{
|
||||
"열쇠": "2-3-3 블록 붙이기(기계)",
|
||||
"이름": "블록 붙이기(기계)",
|
||||
"기준": "㎡당",
|
||||
"출처": "건설품셈 토목 2-3-3",
|
||||
"조건": {},
|
||||
"값칸": { "특별인부": "인", "보통인부": "인", "크레인": "시간", "크레인규격": "글" },
|
||||
"줄": [
|
||||
{ "특별인부": 0.017, "보통인부": 0.007, "크레인": 0.048, "크레인규격": "10 톤" }
|
||||
],
|
||||
"주": [
|
||||
"① 본 품은 하천제방에 장비를 사용하여 호안블록을 설치하는 기준이다.",
|
||||
"② 본 품은 호안블록 설치, 철물 연결 작업이 포함된 것이다.",
|
||||
"③ 비탈면 고르기, 흙 채움 및 잔디심기가 필요한 경우에는 별도 계상한다.",
|
||||
"④ 현장여건에 따라 크레인을 굴착기(규격 0.2㎥, 사용시간 0.063hr)로 적용할 수 있다."
|
||||
]
|
||||
},
|
||||
{
|
||||
"열쇠": "2-3-3 블록 붙이기(기계) 계수 주④",
|
||||
"이름": "블록 붙이기(기계) 계수 주④",
|
||||
"기준": "",
|
||||
"출처": "건설품셈 토목 2-3-3 주④",
|
||||
"그룹": "계수",
|
||||
"조건": { "구분": "고르기" },
|
||||
"값칸": { "값": "구분 따름", "단위": "글" },
|
||||
"줄": [
|
||||
{ "구분": "크레인 대체 굴착기 규격", "값": 0.2, "단위": "㎥" },
|
||||
{ "구분": "크레인 대체 굴착기 사용시간", "값": 0.063, "단위": "hr" }
|
||||
],
|
||||
"주": []
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user