Merge remote-tracking branch 'origin/sub_laptop_4' into sub_laptop_2
This commit is contained in:
@@ -0,0 +1,307 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""건설품셈 공통 제3장 md 읽기 · 표 만들기 · 쓰기 도구 (build_소요량_건설품셈_공통3장.py 가 씀)."""
|
||||
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
SRC = (
|
||||
ROOT / "resources/knowledge/original/원가계산/건설공사_표준품셈/본문/01_공통부문/제03장_토공사"
|
||||
)
|
||||
OUT = ROOT / "resources/master_data/소요량_건설품셈_공통3장.json"
|
||||
DIV = "건설품셈 공통 "
|
||||
|
||||
|
||||
class Raw(str):
|
||||
"""원문 수 글자 그대로(0.040 유지)."""
|
||||
|
||||
|
||||
NUMRE = re.compile(r"^\d[\d,]*(\.\d+)?$")
|
||||
|
||||
|
||||
def N(x):
|
||||
x = x.strip()
|
||||
if not NUMRE.match(x):
|
||||
raise ValueError(f"수 아님: {x!r}")
|
||||
return Raw(x.replace(",", ""))
|
||||
|
||||
|
||||
def R(x):
|
||||
return Raw(x.replace(",", ""))
|
||||
|
||||
|
||||
def join(cell):
|
||||
parts = [p.strip() for p in cell.split("<br>")]
|
||||
out = parts[0]
|
||||
for p in parts[1:]:
|
||||
out += ("" if out.endswith("∼") else " ") + p
|
||||
return out
|
||||
|
||||
|
||||
def unesc(s):
|
||||
return re.sub(r"\\([\\*\-#>.|])", r"\1", s).replace("<br>", " ")
|
||||
|
||||
|
||||
# ── md 읽기 ────────────────────────────────────────────────
|
||||
class Tab:
|
||||
def __init__(self, rows, base):
|
||||
self.rows, self.base = rows, base
|
||||
|
||||
|
||||
def body_lines(path):
|
||||
lines = path.read_text(encoding="utf-8").split("\n")
|
||||
at = [i for i, x in enumerate(lines) if x.strip() == "---"]
|
||||
return lines[at[1] + 1 :]
|
||||
|
||||
|
||||
def parse_tables(path):
|
||||
L = body_lines(path)
|
||||
out, i = [], 0
|
||||
while i < len(L):
|
||||
if not L[i].startswith("|"):
|
||||
i += 1
|
||||
continue
|
||||
j = i
|
||||
while j < len(L) and L[j].startswith("|"):
|
||||
j += 1
|
||||
rows = []
|
||||
for ln in L[i:j]:
|
||||
cells = [c.strip() for c in ln.strip().strip("|").split("|")]
|
||||
if all(re.fullmatch(r":?-+:?", c) for c in cells):
|
||||
continue
|
||||
rows.append(cells)
|
||||
k = i - 1
|
||||
while k >= 0 and not L[k].strip():
|
||||
k -= 1
|
||||
m = re.fullmatch(r"\((.*)\)", L[k].strip()) if k >= 0 else None
|
||||
out.append(Tab(rows, m.group(1) if m else ""))
|
||||
i = j
|
||||
return out
|
||||
|
||||
|
||||
FILES = {p.name[:4]: p for p in sorted(SRC.glob("3-*.md"))}
|
||||
TABS = {k: parse_tables(p) for k, p in FILES.items()}
|
||||
|
||||
|
||||
def sec_lines(ident):
|
||||
"""항 번호(3-2-1)의 본문 줄(제목 포함, 같은·윗 제목 전까지)."""
|
||||
L = body_lines(FILES[f"3-{int(ident.split('-')[1]):02d}"])
|
||||
for at, ln in enumerate(L):
|
||||
m = re.match(rf"^(#+)\s*{re.escape(ident)}(\s|\(|$)", ln)
|
||||
if m:
|
||||
end = len(L)
|
||||
for k in range(at + 1, len(L)):
|
||||
h = re.match(r"^(#+)\s", L[k])
|
||||
if h and len(h.group(1)) <= len(m.group(1)):
|
||||
end = k
|
||||
break
|
||||
return L[at:end]
|
||||
raise KeyError(ident)
|
||||
|
||||
|
||||
def sec_text(ident):
|
||||
return unesc("\n".join(sec_lines(ident)))
|
||||
|
||||
|
||||
def notes(ident, n=0):
|
||||
"""항의 n번째 [주] 묶음 줄들(원문 글)."""
|
||||
blocks, cur = [], None
|
||||
for ln in sec_lines(ident):
|
||||
s = ln.strip()
|
||||
if s == "[주]":
|
||||
cur = []
|
||||
blocks.append(cur)
|
||||
continue
|
||||
if cur is None:
|
||||
continue
|
||||
if re.match(r"^#", s) or s.startswith("[참고") or re.match(r"^[가-힣]\. ", s):
|
||||
cur = None
|
||||
continue
|
||||
if not s or s.startswith(("|", "<!--", "![")) or re.fullmatch(r"\(.*\)", s):
|
||||
continue
|
||||
cur.append(unesc(s))
|
||||
return blocks[n]
|
||||
|
||||
|
||||
def bigo(t, r, c):
|
||||
items = []
|
||||
for p in (x.strip() for x in t.rows[r][c].split("<br>")):
|
||||
if p.startswith("- ") or not items:
|
||||
items.append(p)
|
||||
else:
|
||||
items[-1] += " " + p
|
||||
return ["비고 " + x for x in items]
|
||||
|
||||
|
||||
def title(ident):
|
||||
ln = re.sub(r"^#+\s*\S+\s+", "", sec_lines(ident)[0])
|
||||
return re.sub(r"\('\d\d[^()]*\)$", "", ln).strip()
|
||||
|
||||
|
||||
# ── 표 만들기 ──────────────────────────────────────────────
|
||||
TABLES, KEYS = [], set()
|
||||
|
||||
|
||||
def add(t, ident, label, cond, cols, rows, note=(), *, src=None, base=None, group=None, rule=None):
|
||||
"""t = 원문 표(기준 밑수용) · label 없으면 항 제목 · src = 출처 꼬리(없으면 항 번호)."""
|
||||
label = label or title(ident)
|
||||
key = f"{ident} {label}"
|
||||
assert key not in KEYS, key
|
||||
KEYS.add(key)
|
||||
base = (t.base if t else "") if base is None else base
|
||||
out = {"열쇠": key, "이름": label, "기준": base, "출처": DIV + (src or ident)}
|
||||
if group:
|
||||
out["그룹"] = group
|
||||
out["조건"] = cond
|
||||
if rule:
|
||||
out["범위규칙"] = rule
|
||||
out.update({"값칸": cols, "줄": rows, "주": list(note)})
|
||||
TABLES.append(out)
|
||||
|
||||
|
||||
def sp(row, spec=1):
|
||||
"""규격 글(없으면 None) — 값 칸 「<이름>규격」 으로 올림(수를 열쇠에 두지 않음)."""
|
||||
return join(row[spec]) if spec is not None and row[spec] not in ("", "-") else None
|
||||
|
||||
|
||||
def crew(t, r0, r1, name_c=0, spec=1, unit=2, qty=3):
|
||||
cols, vals, prev = {}, {}, ""
|
||||
for r in range(r0, r1 + 1):
|
||||
row = t.rows[r]
|
||||
n, u = join(row[name_c]), row[unit]
|
||||
if u == "〃":
|
||||
u = prev
|
||||
elif u:
|
||||
prev = u
|
||||
cols[n] = u
|
||||
s_ = sp(row, spec)
|
||||
if s_:
|
||||
cols[n + "규격"] = "글"
|
||||
if row[qty] not in ("", "-"):
|
||||
assert n not in vals, n
|
||||
vals[n] = N(row[qty])
|
||||
if s_:
|
||||
vals[n + "규격"] = s_
|
||||
return cols, vals
|
||||
|
||||
|
||||
def sg_unit(t):
|
||||
for c in t.rows[0] + t.rows[1]:
|
||||
m = re.search(r"시공량\s*\((.*?)\)", join(c))
|
||||
if m:
|
||||
return m.group(1)
|
||||
raise ValueError("시공량 단위")
|
||||
|
||||
|
||||
def across(t, cond, hr, first, last, c0, nc, **kw):
|
||||
"""조건이 가로 칸(c0..)에 있고 시공량이 첫 몸 줄에 한 번 있는 표."""
|
||||
cols, vals = crew(t, first, last, **kw)
|
||||
cols["시공량"] = sg_unit(t)
|
||||
rows = []
|
||||
for k in range(nc):
|
||||
rows.append({cond: join(t.rows[hr][c0 + k]), **vals, "시공량": N(t.rows[first][c0 + k])})
|
||||
return cols, rows
|
||||
|
||||
|
||||
def grouped(t, cond, first, last, c0=3, step=2):
|
||||
"""조건마다 (수량, 시공량) 두 칸."""
|
||||
cols, _ = crew(t, first, last)
|
||||
cols["시공량"] = sg_unit(t)
|
||||
rows = []
|
||||
for k in range((len(t.rows[0]) - c0) // step):
|
||||
d = {cond: join(t.rows[0][c0 + step * k])}
|
||||
for r in range(first, last + 1):
|
||||
row, q = t.rows[r], t.rows[r][c0 + step * k]
|
||||
if q not in ("", "-"):
|
||||
d[join(row[0])] = N(q)
|
||||
if sp(row):
|
||||
d[join(row[0]) + "규격"] = sp(row)
|
||||
d["시공량"] = N(t.rows[first][c0 + step * k + 1])
|
||||
rows.append(d)
|
||||
return cols, rows
|
||||
|
||||
|
||||
def one(t, first, last, sg=None, **kw):
|
||||
"""조건 없는 표 — 줄 하나. sg = 시공량 칸(열 번호)."""
|
||||
cols, vals = crew(t, first, last, **kw)
|
||||
if sg is not None:
|
||||
cols["시공량"] = sg_unit(t)
|
||||
vals["시공량"] = N(t.rows[first][sg])
|
||||
return cols, [vals]
|
||||
|
||||
|
||||
def rate(t, cond):
|
||||
"""머리 = 조건, 둘째 줄 = 값 칸 하나(요율 %)."""
|
||||
label = t.rows[1][0]
|
||||
rows = [
|
||||
{cond: t.rows[0][c], label: R(t.rows[1][c].rstrip("%"))} for c in range(1, len(t.rows[0]))
|
||||
]
|
||||
return {label: "%"}, rows
|
||||
|
||||
|
||||
def coef(ident, label, suffix, rows):
|
||||
"""글 속 계산용 수 — 정규식으로 본문에서 읽음. (구분, 식, 단위[, 원문식])"""
|
||||
text = sec_text(ident)
|
||||
out, has_orig = [], False
|
||||
for lab, pat, unit, *orig in rows:
|
||||
m = re.search(pat, text)
|
||||
if not m:
|
||||
raise ValueError(f"{ident} {lab}: {pat}")
|
||||
g = [R(x) for x in m.groups()]
|
||||
d = {"구분": lab, "값": g[0] if len(g) == 1 else g, "단위": unit}
|
||||
if orig:
|
||||
d["값원문"], has_orig = re.search(orig[0], text).group(0), True
|
||||
out.append(d)
|
||||
cols = {"값": "구분 따름", "단위": "글"}
|
||||
if has_orig:
|
||||
cols["값원문"] = "글"
|
||||
add(None, ident, label, {"구분": "고르기"}, cols, out, src=f"{ident} {suffix}", group="계수")
|
||||
|
||||
|
||||
def rng(text):
|
||||
nums = [R(x) for x in re.findall(r"\d[\d,]*(?:\.\d+)?", text)]
|
||||
lo, hi = "이상" in text, "미만" in text or "이하" in text
|
||||
if lo and hi:
|
||||
return [nums[0], nums[1]]
|
||||
return [None, nums[0]] if hi else [nums[0], None]
|
||||
|
||||
|
||||
PCT = "([\\d.]+)%"
|
||||
LOSE = ("공구손료 및 경장비 인력품 비율", r"인력품의 ([\d.]+)%", "%")
|
||||
|
||||
|
||||
# ── 쓰기 ───────────────────────────────────────────────────
|
||||
def js(v):
|
||||
if isinstance(v, Raw):
|
||||
return str(v)
|
||||
if v is None:
|
||||
return "null"
|
||||
if isinstance(v, str):
|
||||
return json.dumps(v, ensure_ascii=False)
|
||||
if isinstance(v, list):
|
||||
return "[" + ", ".join(js(x) for x in v) + "]"
|
||||
if isinstance(v, dict):
|
||||
return "{ " + ", ".join(f"{js(k)}: {js(x)}" for k, x in v.items()) + " }" if v else "{}"
|
||||
raise TypeError(v)
|
||||
|
||||
|
||||
def write():
|
||||
L = ["{", ' "그룹": "소요량",', ' "원문": "건설품셈",', ' "판": "2026",', ' "표": [']
|
||||
for ti, t in enumerate(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(TABLES) - 1 else ""))
|
||||
L += [" ]", "}", ""]
|
||||
OUT.write_text("\n".join(L), encoding="utf-8", newline="\n")
|
||||
@@ -0,0 +1,149 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""건설품셈 공통 제2장 — 글 속 계산용 수(할증률·한도·손율 기준·식 상수 …)를 본문 md 줄에서 뽑아 계수 표로.
|
||||
|
||||
build_소요량_건설품셈_공통2장.py 가 부름. 값은 아래 찾을 식으로 md 글에서 읽음(손으로 적지 않음).
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
CIRC = "①②③④⑤⑥⑦⑧⑨⑩⑪⑫"
|
||||
|
||||
# ── 글 속 계수 — (구분, 찾을 식, 단위, 종류) · 종류 n=수 · r=범위(두 수) · x=식 글자 그대로(+원문) ─────────────
|
||||
TOOL = r"공구손료 및 경장비\(.*?\)의 기계경비는 인력품[의이] (\d+)%로 계상한다"
|
||||
# fmt: off
|
||||
LIFT_TEXT = r"""
|
||||
2-1-2|시멘트 창고 면적식 계수(A=0.4×N/n)|A=(0\.4)×N||n
|
||||
2-1-2|시멘트 쌓기 단수 최고|쌓기 단수\(최고 (\d+)포대\)|포대|n
|
||||
2-1-2|시멘트 창고 전량 저장 기준 시멘트량|시멘트량이 (\d+)포대 이내|포대|n
|
||||
2-1-2|시멘트량 기준 이상일 때 저장 비율(전량 대비)|전량의 (1/3)||x
|
||||
2-1-2|동력소 및 변전소 면적식 계수(A=3.3√W)|A=(3\.3)√W||n
|
||||
2-1-2|건설기계 주기장 실제 대당 소요면적 배율|소요면적의 (1\.2)배|배|n
|
||||
2-1-2|건설기계 주기장 면적 비율(최대 소요면적 기준)|소요면적의 (70)%|%|n
|
||||
2-1-2|가설전등 등당 전력|등당 (100)W|W|n
|
||||
2-2-2|철물·강재류 길이 한도(이하)|길이가 (2)m 이하|m|n
|
||||
2-2-2|철물·강재류 길이 한도 이하 1회 사용 후 손율|손율은 (100)%로 계상|%|n
|
||||
2-2-2|강재 절단 안 함 손료식의 상수(1+재료의 할증률)|\((1)\+재료의 할증률\)||n
|
||||
2-2-5|강재비계 내구년한|내구년한 (5\.5)년|년|n
|
||||
2-4-1|지주간격|지주간격 (2\.0)m|m|n
|
||||
2-4-2|지주간격|지주간격 (2\.0)m|m|n
|
||||
2-5-1|규준틀 높이|높이 (0\.5)m|m|n
|
||||
2-5-2|규준틀 높이|높이 (2\.4)m|m|n
|
||||
2-6-1|설치높이 한도|설치높이 (4\.2)m까지|m|n
|
||||
2-6-1|잡재료 및 소모재료율(주재료비 기준)|주재료비의 (5)%|%|n
|
||||
2-6-2|설치높이 한도|설치높이 (4\.2)m까지|m|n
|
||||
2-6-2|잡재료 및 소모재료율(주재료비 기준)|주재료비의 (5)%|%|n
|
||||
2-6-5|설치높이 한도|설치높이 (5)m이하|m|n
|
||||
2-7-1|비계주위 보호망 미시공 시 시공량 증가율|본 시공량의 (8)%를 증하여|%|n
|
||||
2-7-1|비계 높이 한도(초과 시 별도 계상)|높이 (30)m 초과|m|n
|
||||
2-7-2|비계주위 보호망 미시공 시 시공량 증가율|본 시공량의 (8)%를 증하여|%|n
|
||||
2-7-2|비계 높이 한도(초과 시 별도 계상)|높이 (30)m 초과|m|n
|
||||
2-7-3|비계주위 보호망 미시공 시 시공량 증가율|본 시공량의 (8)%를 증하여|%|n
|
||||
2-7-3|비계 높이 한도(초과 시 별도 계상)|높이 (30)m 초과|m|n
|
||||
2-7-4|비계 높이 한도(초과 시 별도 계상)|높이 (20)m 초과|m|n
|
||||
2-7-5|수평비계 높이 한도|높이 (10)m이하|m|n
|
||||
2-7-7|가설계단 높이 한도|높이 (6)m이하에서|m|n
|
||||
2-7-7|강관 외경|φ (48\.6)㎜|㎜|n
|
||||
2-7-7|가설계단 폭 한도|폭은 (0\.9)m이하|m|n
|
||||
2-7-8|가설계단 폭 한도|폭은 (0\.9)m이하|m|n
|
||||
2-8-1|낙하물방지망 수평방향 한도|수평방향 (3)m이하|m|n
|
||||
2-8-1|그물망 손율(1회사용 후)|손율은 1회사용 후 (100)%|%|n
|
||||
2-8-2|낙하물방지망 수평방향 한도|수평방향 (3)m이하|m|n
|
||||
2-8-2|그물망 손율(1회사용 후)|손율은 1회사용 후 (100)%|%|n
|
||||
2-8-3|낙하물방지망 수평방향 한도|수평방향 (4)m이하|m|n
|
||||
2-8-7|그물망 손율(1회 사용 후)|손율은 1회 사용 후 (100)%|%|n
|
||||
2-8-8|보호망 손율(1회사용 후)|손율은 1회사용 후 (100)%|%|n
|
||||
2-8-9|보호망 손율(1회사용 후)|손율은 1회사용 후 (100)%|%|n
|
||||
2-8-12|난간대 길이 한도|길이 (2\.5)m이하|m|n
|
||||
2-8-14|난간틀 높이 한도|높이 (1\.4)m이하|m|n
|
||||
2-8-14|난간틀 길이 한도|길이 (1\.3)m이하|m|n
|
||||
2-8-15|추락방호망 규격(면적, 이하)|규격은 (\d+)~(\d+)㎡이하|㎡|r
|
||||
2-9-1|울타리 높이|울타리 높이 (2\.0)m|m|n
|
||||
2-9-2|건설용리프트 규격(싱글)|싱글 (1\.2)ton|ton|n
|
||||
2-9-3|통로 높이 한도|높이 (3\.0)m이하|m|n
|
||||
2-9-3|통로 폭|폭 (2\.0)m 기준|m|n
|
||||
2-9-8|PE가설휀스 길이|L(1\.5)xH|m|n
|
||||
2-9-8|PE가설휀스 높이|xH(0\.9)m|m|n
|
||||
2-9-9|가림막 가설휀스 길이|L(2\.0)xH|m|n
|
||||
2-9-9|가림막 가설휀스 높이|xH(\d\.\d)~(\d\.\d)m|m|r
|
||||
2-10-1|보호막 손율(1회사용 후)|손율은 1회사용 후 (100)%|%|n
|
||||
2-10-2|방진망 손율(1회사용 후)|손율은 1회사용 후 (100)%|%|n
|
||||
2-10-3|터널방음문 차로 한도(이하)|\((3)차로 이하\)|차로|n
|
||||
2-10-4|간이흙막이 개당 길이 한도|길이는 (3\.0)m 이하|m|n
|
||||
2-10-4|간이흙막이 폭 한도|폭은 (2\.0)m이하|m|n
|
||||
2-10-7|방호벽 간격|간격 (2)m|m|n
|
||||
2-11-1|보양재 손율|손율은 (100)%를 적용|%|n
|
||||
2-11-5|살수두께|살수두께는 (1\.5)㎜/회|㎜/회|n
|
||||
2-11-5|살수폭|살수폭은 (4\.0)m|m|n
|
||||
2-11-5|살수면적 계산 예 - 폭|= (6)m ×|m|n
|
||||
2-11-5|살수면적 계산 예 - 길이|× (100)m ×|m|n
|
||||
2-11-5|살수면적 계산 예 - 1일 살수 횟수|× (5)회/일|회/일|n
|
||||
2-11-5|살수면적 계산 예 - 살수 일수|× (10)일|일|n
|
||||
2-11-5|살수면적 계산 예 - 살수면적|= (30,000)㎡|㎡|n
|
||||
2-11-7|슬러지함 규격|슬러지함 (2\.0×1\.2×1\.2)m|m|x
|
||||
2-12-1|건설용리프트 규격(싱글)|싱글 (1\.2)ton|ton|n
|
||||
2-12-2|건설용리프트 규격(싱글)|싱글 (1\.2)ton|ton|n
|
||||
2-12-2|가산 기준 층(이상)|(19)층 이상|층|n
|
||||
2-12-2|가산 층 간격|매 (3)층 증가마다|층|n
|
||||
2-12-2|층 간격마다 가산율|증가마다 (4)%씩|%|n
|
||||
2-12-2|층고 기준(층수 환산)|층고를 (3\.6)m로|m|n
|
||||
2-12-4|강관장 기준|강관장 (6\.0)m|m|n
|
||||
"""
|
||||
# fmt: on
|
||||
LIFT: dict[str, list] = {}
|
||||
for _ln in LIFT_TEXT.strip().splitlines():
|
||||
_it, *_e = _ln.split("|")
|
||||
LIFT.setdefault(_it, []).append(tuple(_e))
|
||||
|
||||
|
||||
def lift_table(item, title, unesc, N, book):
|
||||
"""그 항 본문 줄에서 계수 표를 만듦(값은 md 글에서 뽑음). 없으면 None."""
|
||||
text = [re.sub(r"\s*<br>\s*", " ", unesc(ln)) for ln in item["lines"]]
|
||||
found = [] # (줄 번호, 차례, 행, 출처 표시)
|
||||
specs = [("공구손료 및 경장비 기계경비(인력품 기준)", TOOL, "%", "n", True)] + [
|
||||
(*e, False) for e in LIFT.get(item["id"], [])
|
||||
]
|
||||
for seq, (name, rx, unit, kind, many) in enumerate(specs):
|
||||
hits = [(k, re.search(rx, ln)) for k, ln in enumerate(text) if re.search(rx, ln)]
|
||||
if not hits:
|
||||
if many:
|
||||
continue
|
||||
raise ValueError(f"글에서 못 찾음 {item['id']} {name} {rx}")
|
||||
if len(hits) > 1:
|
||||
raise ValueError(f"여럿 찾음 {item['id']} {name} {rx}")
|
||||
k, m = hits[0]
|
||||
row = {"구분": name}
|
||||
row["값"] = (
|
||||
[N(m.group(1)), N(m.group(2))]
|
||||
if kind == "r"
|
||||
else (m.group(1) if kind == "x" else N(m.group(1)))
|
||||
)
|
||||
if kind == "x":
|
||||
row["값원문"] = m.group(0)
|
||||
if unit:
|
||||
row["단위"] = unit
|
||||
ln = text[k]
|
||||
if ln.startswith("|"):
|
||||
ref = "표"
|
||||
elif ln.startswith("※"):
|
||||
ref = "※"
|
||||
elif ln.startswith(">"):
|
||||
ref = "예시"
|
||||
else:
|
||||
back = next((text[b][0] for b in range(k, -1, -1) if text[b][:1] in CIRC), "")
|
||||
ref = "주" + back
|
||||
found.append((k, seq, row, ref))
|
||||
if not found:
|
||||
return None
|
||||
found.sort(key=lambda x: x[:2])
|
||||
refs = list(dict.fromkeys(f[3] for f in found))
|
||||
return {
|
||||
"열쇠": f"{item['id']} 계수",
|
||||
"이름": f"{title} - 글 속 계수",
|
||||
"기준": "",
|
||||
"출처": f"{book} {item['id']} " + "·".join(refs),
|
||||
"그룹": "계수",
|
||||
"조건": {"구분": "고르기"},
|
||||
"값칸": {"값": "구분 따름", "단위": "글"},
|
||||
"줄": [f[2] for f in found],
|
||||
"주": [],
|
||||
}
|
||||
@@ -0,0 +1,641 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""계수_산림품셈_01장.json 에 1-1-3 · 1-2 · 1-6 · 1-7 표를 더함 (기존 표는 글자 그대로 보존).
|
||||
|
||||
돌리기: ./venv/Scripts/python.exe resources/master_data/scripts/build_계수_산림품셈_01장_추가.py
|
||||
- 1-2 · 1-1-3 표는 1-3-1 표 앞에, 1-6 · 1-7 표는 맨 뒤에 넣음. 이미 넣었으면 멈춤.
|
||||
- md 표는 본문 md 에서 읽어 옮기고, 글 속 계산용 수는 아래 손 표.
|
||||
- 1-5 는 표도 수도 없어 옮길 것 없음.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
from pathlib import Path
|
||||
|
||||
HERE = Path(__file__).resolve().parent
|
||||
TARGET = HERE.parent / "계수_산림품셈_01장.json"
|
||||
BODY = HERE.parents[2] / "resources/knowledge/original/원가계산/산림_표준품셈/본문/제01장_적용기준"
|
||||
SRC = "산림품셈 "
|
||||
|
||||
|
||||
class N(str):
|
||||
"""원문 그대로의 수 글자."""
|
||||
|
||||
|
||||
def raw(v) -> str:
|
||||
if v is None:
|
||||
return "null"
|
||||
if isinstance(v, N):
|
||||
return str(v)
|
||||
if isinstance(v, list):
|
||||
return "[" + ", ".join(raw(x) for x in v) + "]"
|
||||
if isinstance(v, int):
|
||||
return str(v)
|
||||
return json.dumps(v, ensure_ascii=False)
|
||||
|
||||
|
||||
def line(d: dict) -> str:
|
||||
return (
|
||||
"{"
|
||||
+ ", ".join(f"{json.dumps(k, ensure_ascii=False)}: {raw(v)}" for k, v in d.items())
|
||||
+ "}"
|
||||
)
|
||||
|
||||
|
||||
def emit(t: dict) -> str:
|
||||
out = [" {"]
|
||||
for k in ("열쇠", "이름"):
|
||||
out.append(f" {json.dumps(k, ensure_ascii=False)}: {raw(t[k])},")
|
||||
out.append(' "기준": "",')
|
||||
out.append(f' "출처": {raw(t["출처"])},')
|
||||
out.append(f' "조건": {line(t["조건"])},')
|
||||
out.append(f' "값칸": {line(t["값칸"])},')
|
||||
if t.get("범위규칙"):
|
||||
out.append(f' "범위규칙": {raw(t["범위규칙"])},')
|
||||
out.append(' "줄": [')
|
||||
out.append(",\n".join(" " + line(r) for r in t["줄"]))
|
||||
out.append(" ],")
|
||||
out.append(f' "주": {raw(t.get("주", []))}')
|
||||
out.append(" }")
|
||||
return "\n".join(out)
|
||||
|
||||
|
||||
# ── md 표 읽기 ────────────────────────────────────────────────────────
|
||||
_CELL = re.compile(r"(?<!\\)\|")
|
||||
_NUM = r"\d[\d,]*(?:\.\d+)?"
|
||||
|
||||
|
||||
def read_md(name: str):
|
||||
"""(줄 목록, 표 목록[(칸 행렬, 끝 줄 번호)])"""
|
||||
lines = (BODY / name).read_text(encoding="utf-8").split("\n")
|
||||
tables, i = [], 0
|
||||
while i < len(lines):
|
||||
if lines[i].startswith("|"):
|
||||
rows = []
|
||||
while i < len(lines) and lines[i].startswith("|"):
|
||||
cells = [c.strip() for c in _CELL.split(lines[i])[1:-1]]
|
||||
if not all(re.fullmatch(r":?-+:?", c) for c in cells):
|
||||
cells = [
|
||||
re.sub(r"<!--.*?-->", "", c).replace("<br>", "\n").strip() for c in cells
|
||||
]
|
||||
rows.append(cells)
|
||||
i += 1
|
||||
tables.append((rows, i))
|
||||
else:
|
||||
i += 1
|
||||
return lines, tables
|
||||
|
||||
|
||||
def notes_after(lines: list[str], end: int) -> list[str]:
|
||||
i = end
|
||||
while i < len(lines) and (not lines[i].strip() or lines[i].startswith("<!--")):
|
||||
i += 1
|
||||
if i >= len(lines) or lines[i].strip() != "[주]":
|
||||
return []
|
||||
out = []
|
||||
for x in lines[i + 1 :]:
|
||||
if not x.strip():
|
||||
break
|
||||
out.append(x.strip())
|
||||
return out
|
||||
|
||||
|
||||
def num(s: str) -> N:
|
||||
return N(s.replace(",", ""))
|
||||
|
||||
|
||||
def conv(cell: str):
|
||||
"""값 칸 → (값, 원문). 값 없음 = (None, None)."""
|
||||
c = cell.strip()
|
||||
if c in ("", "-"):
|
||||
return None, None
|
||||
if re.fullmatch(_NUM, c):
|
||||
return num(c), None
|
||||
m = re.fullmatch(rf"({_NUM})\s*[∼~~]\s*({_NUM})", c)
|
||||
if m:
|
||||
return [num(m.group(1)), num(m.group(2))], None
|
||||
m = re.fullmatch(rf"\(({_NUM})\)", c)
|
||||
if m:
|
||||
return num(m.group(1)), c
|
||||
m = re.match(rf"({_NUM})\n", c)
|
||||
if m:
|
||||
return num(m.group(1)), c
|
||||
return c, None
|
||||
|
||||
|
||||
def put(row: dict, col: str, cell: str):
|
||||
v, orig = conv(cell)
|
||||
if v is not None:
|
||||
row[col] = v
|
||||
if orig is not None:
|
||||
row[col + "원문"] = orig
|
||||
|
||||
|
||||
def pairs(rows: list[dict], key: str) -> None:
|
||||
"""같은 종목이 잇따르는 무리가 둘 이상이면 짝 번호를 매김."""
|
||||
i = 0
|
||||
while i < len(rows):
|
||||
j = i
|
||||
while j < len(rows) and rows[j][key] == rows[i][key]:
|
||||
j += 1
|
||||
if j - i > 1:
|
||||
for n, r in enumerate(rows[i:j], start=1):
|
||||
items = list(r.items())
|
||||
items.insert(1, ("짝", "가나다라마바사아자차"[n - 1]))
|
||||
r.clear()
|
||||
r.update(items)
|
||||
i = j
|
||||
|
||||
|
||||
def table(key, name, src, cond, cols, rows, notes=(), rule=""):
|
||||
return {
|
||||
"열쇠": key,
|
||||
"이름": name,
|
||||
"출처": SRC + src,
|
||||
"조건": cond,
|
||||
"값칸": cols,
|
||||
"줄": rows,
|
||||
"주": list(notes),
|
||||
"범위규칙": rule,
|
||||
}
|
||||
|
||||
|
||||
# ── md 표 옮기기 ──────────────────────────────────────────────────────
|
||||
def from_1_02() -> list[dict]:
|
||||
lines, tabs = read_md("1-02_설계_및_수량.md")
|
||||
T = {i: t for i, t in enumerate(tabs)}
|
||||
out = {}
|
||||
|
||||
# 0 설계서의 단위 및 소수의 표준
|
||||
rows, prev = [], ""
|
||||
for c in T[0][0][2:]:
|
||||
prev = c[0] or prev
|
||||
r = {"종목": prev}
|
||||
for col, cell in zip(("규격단위", "규격소수자리", "수량단위", "수량소수자리"), c[1:5]):
|
||||
v, _ = conv(cell)
|
||||
if v is not None:
|
||||
r[col] = v
|
||||
rows.append(r)
|
||||
pairs(rows, "종목")
|
||||
out["단위"] = table(
|
||||
"1-2-2 설계서의 단위 및 소수의 표준",
|
||||
"설계서의 단위 및 소수의 표준",
|
||||
"1-2-2 1",
|
||||
{"종목": "고르기"},
|
||||
{"규격단위": "글", "규격소수자리": "자리", "수량단위": "글", "수량소수자리": "자리"},
|
||||
rows,
|
||||
notes_after(lines, T[0][1]),
|
||||
)
|
||||
|
||||
# 1 금액의 단위 표준
|
||||
rows = []
|
||||
for c in T[1][0][1:]:
|
||||
r = {"종목": c[0], "단위표기": c[1]}
|
||||
put(r, "지위", c[2])
|
||||
r["비고"] = c[3]
|
||||
rows.append(r)
|
||||
out["금액"] = table(
|
||||
"1-2-2 금액의 단위 표준",
|
||||
"금액의 단위 표준",
|
||||
"1-2-2 2",
|
||||
{"종목": "고르기"},
|
||||
{"단위표기": "글", "지위": "원", "비고": "글"},
|
||||
rows,
|
||||
notes_after(lines, T[1][1]),
|
||||
)
|
||||
|
||||
# 2 체적의 변화 식 L · C
|
||||
rows = []
|
||||
for sym, col in (("L", 1), ("C", 3)):
|
||||
top, bottom = T[2][0][0][col], T[2][0][1][col]
|
||||
rows.append({"기호": sym, "식": f"{top}/{bottom}"})
|
||||
out["식"] = table(
|
||||
"1-2-3 체적의 변화 식",
|
||||
"체적의 변화 — 흐트러진상태 L · 다져진상태 C",
|
||||
"1-2-3 3 나",
|
||||
{"기호": "고르기"},
|
||||
{"식": "식"},
|
||||
rows,
|
||||
)
|
||||
|
||||
# 3 체적의 변화율
|
||||
rows = []
|
||||
for c in T[3][0][1:]:
|
||||
r = {"종별": c[0]}
|
||||
put(r, "L", c[1])
|
||||
put(r, "C", c[2])
|
||||
rows.append(r)
|
||||
out["변화율"] = table(
|
||||
"1-2-3 체적의 변화율",
|
||||
"체적의 변화율 L · C",
|
||||
"1-2-3 3 다",
|
||||
{"종별": "고르기"},
|
||||
{"L": "", "C": ""},
|
||||
rows,
|
||||
notes_after(lines, T[3][1]),
|
||||
)
|
||||
|
||||
# 4 체적환산계수 f
|
||||
head = T[4][0][0]
|
||||
cols = [h for h in head[1:]]
|
||||
rows = []
|
||||
for c in T[4][0][2:]:
|
||||
r = {"기준이 되는 q": c[0]}
|
||||
for col, cell in zip(cols, c[1:]):
|
||||
v, _ = conv(cell)
|
||||
if v is not None:
|
||||
r[col] = v
|
||||
rows.append(r)
|
||||
out["f"] = table(
|
||||
"1-2-3 체적환산계수 f",
|
||||
"체적환산계수(f) — 구하는 Q ÷ 기준이 되는 q",
|
||||
"1-2-3 3 라",
|
||||
{"기준이 되는 q": "고르기"},
|
||||
{h: "" for h in cols},
|
||||
rows,
|
||||
)
|
||||
|
||||
# 5 사용고재 공제율
|
||||
rows = []
|
||||
for c in T[5][0][1:]:
|
||||
r = {"품명": c[0]}
|
||||
put(r, "공제율", c[1].replace("%", ""))
|
||||
rows.append(r)
|
||||
out["공제"] = table(
|
||||
"1-2-4 사용 고재 등 발생재 공제율",
|
||||
"사용 고재 등 발생재 공제율",
|
||||
"1-2-4 5",
|
||||
{"품명": "고르기"},
|
||||
{"공제율": "%"},
|
||||
rows,
|
||||
notes_after(lines, T[5][1]),
|
||||
)
|
||||
|
||||
# 6 화물 자동차 적재량
|
||||
hdr = [h.replace("\n", " ") for h in T[6][0][1][3:7]]
|
||||
rows, kind, unit = [], "", ""
|
||||
for c in T[6][0][2:]:
|
||||
kind = c[0] or kind
|
||||
unit = c[2] if c[2] not in ("", """) else unit
|
||||
r = {"종별": kind}
|
||||
if c[1]:
|
||||
r["규격"] = c[1]
|
||||
r["단위"] = unit
|
||||
for col, cell in zip(hdr, c[3:7]):
|
||||
put(r, col, cell)
|
||||
if c[7]:
|
||||
r["비고"] = c[7]
|
||||
rows.append(r)
|
||||
pairs(rows, "종별")
|
||||
out["적재"] = table(
|
||||
"1-2-7 화물자동차 적재량",
|
||||
"화물자동차의 적재량(일반 기준)",
|
||||
"1-2-7 3 라",
|
||||
{"종별": "고르기", "규격": "고르기"},
|
||||
{**{h: "" for h in hdr}, "비고": "글"},
|
||||
rows,
|
||||
)
|
||||
return out
|
||||
|
||||
|
||||
def from_1_06() -> dict:
|
||||
lines, tabs = read_md("1-06_원가_작성기준.md")
|
||||
rows, a, b = [], "", ""
|
||||
for n, c in enumerate(tabs[0][0][2:], start=2):
|
||||
if c[0] and c[0] != a:
|
||||
a, b = c[0], ""
|
||||
b = c[1].replace("\n", "") or b
|
||||
name = c[2].replace("\n", "")
|
||||
method, rate, basis = c[3], c[4], c[5]
|
||||
if 10 <= n <= 13:
|
||||
basis = "관련 법령의 보험요율 적용"
|
||||
r = {"비목 1단": a}
|
||||
if a == "순원가":
|
||||
r["비목 2단"] = b
|
||||
r["비목 3단"] = name
|
||||
r_vals = {}
|
||||
if method:
|
||||
r_vals["적용방법"] = method
|
||||
if rate:
|
||||
put(r_vals, "요율", rate.replace("%", ""))
|
||||
if basis:
|
||||
r_vals["적용기준"] = basis
|
||||
if not r_vals:
|
||||
continue
|
||||
r.update(r_vals)
|
||||
rows.append(r)
|
||||
return table(
|
||||
"1-6 원가 구성 비목별 적용기준",
|
||||
"원가 구성 비목별 적용기준",
|
||||
"1-6 라",
|
||||
{"비목 1단": "고르기", "비목 2단": "고르기", "비목 3단": "고르기"},
|
||||
{"적용방법": "글", "요율": "%", "적용기준": "글"},
|
||||
rows,
|
||||
notes_after(lines, tabs[0][1]),
|
||||
)
|
||||
|
||||
|
||||
def from_1_07() -> list[dict]:
|
||||
lines, tabs = read_md("1-07_기타사항.md")
|
||||
rows = [{"사용횟수(회)": num(c[0].replace("회", "")), "구조물": c[1]} for c in tabs[0][0][1:]]
|
||||
t1 = table(
|
||||
"1-7-1 거푸집 사용횟수",
|
||||
"거푸집 사용횟수",
|
||||
"1-7-1",
|
||||
{"사용횟수(회)": "수"},
|
||||
{"구조물": "글"},
|
||||
rows,
|
||||
["2. 극히 간단한 구조는 6회 이상을 적용할 수 있으며, 품은 현행품의 비율을 적용한다."],
|
||||
)
|
||||
rows = []
|
||||
for c in tabs[1][0][1:]:
|
||||
r = {"구분": c[0], "단위": c[1]}
|
||||
m = (
|
||||
re.fullmatch(rf"({_NUM})[∼~~]({_NUM})%", c[2].split("의 ")[-1])
|
||||
if "%" in c[2] or "%" in c[2]
|
||||
else None
|
||||
)
|
||||
if m:
|
||||
r["수량"] = [num(m.group(1)), num(m.group(2))]
|
||||
r["수량원문"] = c[2]
|
||||
else:
|
||||
put(r, "수량", c[2])
|
||||
rows.append(r)
|
||||
t2 = table(
|
||||
"1-7-4 공사용수",
|
||||
"공사용수",
|
||||
"1-7-4",
|
||||
{"구분": "고르기"},
|
||||
{"수량": ""},
|
||||
rows,
|
||||
notes_after(lines, tabs[1][1]),
|
||||
)
|
||||
return [t1, t2]
|
||||
|
||||
|
||||
# ── 글 속 계산용 수 ───────────────────────────────────────────────────
|
||||
def from_text() -> dict:
|
||||
n = N
|
||||
t = {}
|
||||
t["1-1"] = table(
|
||||
"1-1-3 일일 작업시간",
|
||||
"적용방법 — 품의 일일 작업시간 기준",
|
||||
"1-1-3 2",
|
||||
{},
|
||||
{"일일 작업시간": "시간"},
|
||||
[{"일일 작업시간": 8}],
|
||||
["본 품셈에서 제시된 품은 일일 작업시간 8시간을 기준으로 한 것이다."],
|
||||
)
|
||||
t["소수"] = table(
|
||||
"1-2-1 소수 처리",
|
||||
"수량의 계산 — 소수 처리 · 유효숫자",
|
||||
"1-2-1 1 · 2 · 6",
|
||||
{"대상": "고르기"},
|
||||
{"반올림 소수자리": "째자리", "표시 소수자리": "째자리", "유효숫자": "자리"},
|
||||
[
|
||||
{
|
||||
"대상": "나무 재적 및 인원",
|
||||
"반올림 소수자리": 3,
|
||||
"반올림 소수자리원문": "소수점 셋째자리에서 반올림",
|
||||
"표시 소수자리": 2,
|
||||
"표시 소수자리원문": "둘째자리로 표시",
|
||||
},
|
||||
{
|
||||
"대상": "헥타(ha) 환산 면적",
|
||||
"반올림 소수자리": 3,
|
||||
"반올림 소수자리원문": "소수점 셋째자리에서 반올림",
|
||||
"표시 소수자리": 2,
|
||||
"표시 소수자리원문": "둘째자리로 표시",
|
||||
},
|
||||
{
|
||||
"대상": "원둘레율·삼각함수·호도",
|
||||
"유효숫자": 3,
|
||||
"유효숫자원문": "유효숫자는 3자리(3位)",
|
||||
},
|
||||
],
|
||||
)
|
||||
t["총축적"] = table(
|
||||
"1-2-1 총축적 계산 기준",
|
||||
"총축적 계산 — 측정 기준",
|
||||
"1-2-1 5",
|
||||
{},
|
||||
{
|
||||
"측정 대상 가슴높이지름": "㎝ 이상",
|
||||
"가슴높이 측정 지상고": "㎝",
|
||||
"가슴높이지름 괄약": "㎝",
|
||||
"표준지 최대크기": "ha 이하",
|
||||
},
|
||||
[
|
||||
{
|
||||
"측정 대상 가슴높이지름": 6,
|
||||
"가슴높이 측정 지상고": 120,
|
||||
"가슴높이지름 괄약": 2,
|
||||
"표준지 최대크기": n("0.04"),
|
||||
}
|
||||
],
|
||||
[
|
||||
"가. 측정 대상입목 : 가슴높이지름 6㎝ 이상의 입목",
|
||||
"나. 가슴높이지름 측정부위 : 지상고 120㎝ 위치의 직경을 말하며, 2㎝ 괄약으로 측정(예 : 8㎝=7㎝ 이상 9㎝ 미만, 10㎝=9㎝ 이상 11㎝ 미만)",
|
||||
"(나) 표준지조사 1) 1개 표준지의 최대크기는 0.04ha 이하로 한다.",
|
||||
],
|
||||
)
|
||||
t["괄약"] = table(
|
||||
"1-2-1 가슴높이지름 괄약 예",
|
||||
"가슴높이지름 2㎝ 괄약 — 대표값별 범위",
|
||||
"1-2-1 5 나",
|
||||
{"괄약 대표값(㎝)": "수"},
|
||||
{"범위": "㎝"},
|
||||
[{"괄약 대표값(㎝)": 8, "범위": [7, 9]}, {"괄약 대표값(㎝)": 10, "범위": [9, 11]}],
|
||||
[],
|
||||
"범위(㎝) 이상·미만 — 「7㎝ 이상 9㎝ 미만」 = [7, 9]",
|
||||
)
|
||||
t["공제"] = table(
|
||||
"1-2-1 공제하지 않는 포장 구조물 자리",
|
||||
"수량 공제 제외 — 포장공종 1개소당 구조물 자리",
|
||||
"1-2-1 9 라",
|
||||
{},
|
||||
{"1개소당 구조물 자리": "㎡ 이하"},
|
||||
[{"1개소당 구조물 자리": n("0.1")}],
|
||||
["라. 포장공종의 1개소당 0.1㎡ 이하의 구조물 자리"],
|
||||
)
|
||||
t["균열"] = table(
|
||||
"1-2-3 암 분류 균열간격",
|
||||
"토질 및 암의 분류 — 암 종류별 균열간격",
|
||||
"1-2-3 2",
|
||||
{"종류": "고르기"},
|
||||
{"균열간격": ""},
|
||||
[
|
||||
{"종류": "풍화암", "균열간격": [1, 10], "단위": "㎝"},
|
||||
{"종류": "연암", "균열간격": [10, 30], "단위": "㎝"},
|
||||
{"종류": "보통암", "균열간격": [30, 50], "단위": "㎝"},
|
||||
{
|
||||
"종류": "경암",
|
||||
"균열간격": [None, 1],
|
||||
"균열간격원문": "균열상태가 1m 이내",
|
||||
"단위": "m",
|
||||
},
|
||||
],
|
||||
[],
|
||||
"균열간격 — 「1∼10㎝」 = [1, 10] · 「1m 이내」 = [null, 1]",
|
||||
)
|
||||
t["형상"] = table(
|
||||
"1-2-3 견치돌·깬돌·깬잡석 형상 비율",
|
||||
"돌재료 분류 — 견치돌 · 깬돌 · 깬잡석 형상 비율",
|
||||
"1-2-3 2 차",
|
||||
{"종류": "고르기"},
|
||||
{"접촉면의 폭": "", "접촉면의 길이": "", "전면 변의 평균길이": ""},
|
||||
[
|
||||
{
|
||||
"종류": "견치돌",
|
||||
"접촉면의 폭": "1/10",
|
||||
"접촉면의 폭원문": "전면 일변 길이의 1/10이상",
|
||||
"접촉면의 길이": "1/2",
|
||||
"접촉면의 길이원문": "일변 평균길이의 1/2 이상",
|
||||
},
|
||||
{
|
||||
"종류": "깬돌",
|
||||
"접촉면의 폭": "1/20",
|
||||
"접촉면의 폭원문": "전면의 일변 평균길이의 약 1/20",
|
||||
"접촉면의 길이": "1/3",
|
||||
"접촉면의 길이원문": "약 1/3",
|
||||
},
|
||||
{
|
||||
"종류": "깬잡석",
|
||||
"전면 변의 평균길이": "2/3",
|
||||
"전면 변의 평균길이원문": "뒷길이의 약 2/3",
|
||||
},
|
||||
],
|
||||
)
|
||||
t["크기"] = table(
|
||||
"1-2-3 돌재료 크기 분류",
|
||||
"돌재료 분류 — 크기 기준",
|
||||
"1-2-3 2 차",
|
||||
{"종류": "고르기"},
|
||||
{"지름": "", "뒷길이": "", "크기": ""},
|
||||
[
|
||||
{"종류": "잡석", "지름": [10, 30], "단위": "㎝"},
|
||||
{
|
||||
"종류": "큰돌",
|
||||
"뒷길이": [60, None],
|
||||
"뒷길이원문": "뒷길이 60㎝이상",
|
||||
"지름": [40, 100],
|
||||
"지름원문": "지름직경이 40∼100㎝ 이하",
|
||||
"단위": "㎝",
|
||||
},
|
||||
{"종류": "호박돌", "지름": [18, None], "단위": "㎝"},
|
||||
{"종류": "조약돌", "크기": [10, 20], "단위": "㎝"},
|
||||
{"종류": "부순돌", "지름": [n("0.5"), 10], "단위": "㎝"},
|
||||
{"종류": "굵은 자갈", "지름": [n("7.5"), 20], "단위": "㎝"},
|
||||
{"종류": "자갈", "지름": [n("0.5"), n("7.5")], "단위": "㎝"},
|
||||
{"종류": "굵은 모래", "지름": [n("0.25"), 2], "단위": "mm"},
|
||||
{"종류": "잔모래", "지름": [n("0.05"), n("0.25")], "단위": "mm"},
|
||||
{
|
||||
"종류": "고로슬래그 부순돌",
|
||||
"크기": [0, 40],
|
||||
"크기원문": "0∼40mm로 파쇄 가공",
|
||||
"단위": "mm",
|
||||
},
|
||||
{
|
||||
"종류": "막돌",
|
||||
"뒷길이": [25, 40],
|
||||
"지름": [20, None],
|
||||
"지름원문": "직경 20㎝ 이상",
|
||||
"단위": "㎝",
|
||||
},
|
||||
],
|
||||
[],
|
||||
"이상·이하 — 「60㎝이상」 = [60, null]",
|
||||
)
|
||||
t["파쇄"] = table(
|
||||
"1-2-3 규격품 채취 시 파쇄돌 발생량",
|
||||
"토취장 및 골재원 — 규격품 채취 시 사용 못 하는 파쇄된 돌 발생량",
|
||||
"1-2-3 4 라",
|
||||
{},
|
||||
{"파쇄된 돌 발생량": "%"},
|
||||
[{"파쇄된 돌 발생량": [10, 40], "파쇄된 돌 발생량원문": "10∼40%를 표준"}],
|
||||
)
|
||||
t["사토"] = table(
|
||||
"1-2-3 사토장 관리 보통인부 가산",
|
||||
"사토장 관리 — 토공운반 Q값 반영 시 보통인부 노임 가산",
|
||||
"1-2-3 5 다",
|
||||
{},
|
||||
{"보통인부 노임 가산": ""},
|
||||
[{"보통인부 노임 가산": "1/8", "보통인부 노임 가산원문": "보통인부 노임의 1/8 가산"}],
|
||||
)
|
||||
t["잡재료"] = table(
|
||||
"1-2-6 잡재료 별도 계상",
|
||||
"공구손료 및 잡재료 — 명시 없는 잡재료 별도 계상 한도",
|
||||
"1-2-6",
|
||||
{},
|
||||
{"주재료비 대비": "%"},
|
||||
[
|
||||
{
|
||||
"주재료비 대비": [2, 5],
|
||||
"주재료비 대비원문": "주재료비(재료비의 할증수량 제외)의 2~5%까지",
|
||||
}
|
||||
],
|
||||
)
|
||||
t["소운반"] = table(
|
||||
"1-2-7 소운반 포함 거리",
|
||||
"운반 — 품에 포함된 소운반 거리",
|
||||
"1-2-7 1 가",
|
||||
{},
|
||||
{"소운반 거리": "m 이내"},
|
||||
[{"소운반 거리": 20}],
|
||||
)
|
||||
t["경사"] = table(
|
||||
"1-2-7 경사면 소운반 거리 환산",
|
||||
"운반 — 경사면 소운반 거리(직고 대 수평거리)",
|
||||
"1-2-7 1 나",
|
||||
{},
|
||||
{"직고": "m", "수평거리": "m"},
|
||||
[{"직고": 1, "수평거리": 6}],
|
||||
)
|
||||
return t
|
||||
|
||||
|
||||
def main() -> None:
|
||||
text = TARGET.read_bytes().decode("utf-8")
|
||||
crlf = "\r\n" in text
|
||||
text = text.replace("\r\n", "\n")
|
||||
if '"열쇠": "1-2-2 ' in text:
|
||||
raise SystemExit("이미 추가됨")
|
||||
A, T = from_1_02(), from_text()
|
||||
front = [
|
||||
T["1-1"],
|
||||
T["소수"],
|
||||
T["총축적"],
|
||||
T["괄약"],
|
||||
T["공제"],
|
||||
A["단위"],
|
||||
A["금액"],
|
||||
T["균열"],
|
||||
T["형상"],
|
||||
T["크기"],
|
||||
A["식"],
|
||||
A["변화율"],
|
||||
A["f"],
|
||||
T["파쇄"],
|
||||
T["사토"],
|
||||
A["공제"],
|
||||
T["잡재료"],
|
||||
T["소운반"],
|
||||
T["경사"],
|
||||
A["적재"],
|
||||
]
|
||||
back = [from_1_06(), *from_1_07()]
|
||||
anchor = ' {\n "열쇠": "1-3-1 콘크리트'
|
||||
at = text.index(anchor)
|
||||
text = text[:at] + ",\n".join(emit(t) for t in front) + ",\n" + text[at:]
|
||||
tail = "\n ]\n}"
|
||||
end = text.rindex(tail)
|
||||
text = text[:end] + ",\n" + ",\n".join(emit(t) for t in back) + text[end:]
|
||||
if crlf:
|
||||
text = text.replace("\n", "\r\n")
|
||||
TARGET.write_bytes(text.encode("utf-8"))
|
||||
print(len(front) + len(back), "표 추가")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,686 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""건설품셈 공통 제2장(가설공사) 본문 md → resources/master_data/소요량_건설품셈_공통2장.json
|
||||
|
||||
값은 본문 md 에서만 읽음(손으로 적지 않음). 표마다 모양이 달라 표별 매핑(SPEC)과 글 속 계수(LIFT)는 손으로 둠.
|
||||
돌리기: ./venv/Scripts/python.exe resources/master_data/scripts/build_소요량_건설품셈_공통2장.py
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from _계수_공통2장 import lift_table
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
SRC = (
|
||||
ROOT
|
||||
/ "resources/knowledge/original/원가계산/건설공사_표준품셈/본문/01_공통부문/제02장_가설공사"
|
||||
)
|
||||
OUT = ROOT / "resources/master_data/소요량_건설품셈_공통2장.json"
|
||||
BOOK = "건설품셈 공통"
|
||||
NUM = r"\d[\d,]*(?:\.\d+)?"
|
||||
|
||||
|
||||
class Num(str):
|
||||
"""원문 그대로 찍을 수(쉼표만 뺌)."""
|
||||
|
||||
|
||||
def N(s: str) -> Num:
|
||||
return Num(s.replace(",", ""))
|
||||
|
||||
|
||||
# ── md 읽기 ───────────────────────────────────────────────────────────
|
||||
def unesc(s: str) -> str:
|
||||
return re.sub(r"\\([\\`*_{}\[\]()#+\-.!>|~])", r"\1", s)
|
||||
|
||||
|
||||
def cl(s: str) -> str:
|
||||
"""칸 글 — <br> 은 「∼」「·」 뒤면 붙이고 아니면 빈칸으로."""
|
||||
s = re.sub(r"(?<=[∼·])\s*<br>\s*", "", s)
|
||||
s = re.sub(r"\s*<br>\s*", " ", s)
|
||||
return unesc(s).strip()
|
||||
|
||||
|
||||
def lab(s: str) -> str:
|
||||
"""머리·줄 이름 — 끝의 「(%)」 뺌."""
|
||||
return re.sub(r"\s*\(%\)$", "", cl(s))
|
||||
|
||||
|
||||
def title_of(s: str) -> str:
|
||||
return re.sub(r"\((?:['‘’]\d\d[^)]*)\)\s*$", "", s).strip()
|
||||
|
||||
|
||||
class Table:
|
||||
def __init__(self, item, mok, mok_no, rows, unit_line):
|
||||
self.item, self.mok, self.mok_no, self.rows, self.base = item, mok, mok_no, rows, unit_line
|
||||
self.notes: list[str] = []
|
||||
|
||||
|
||||
# 주 줄 붙일 자리 바꿈 — (항, 줄 머리) → 표 번호(0부터) · None = 이름에 쓴 제목 줄이라 뺌
|
||||
ATTACH = {
|
||||
("2-1-2", "① 가설건물규모"): 1,
|
||||
("2-1-2", "② 식당"): 2,
|
||||
("2-1-2", "③ 자재창고"): None,
|
||||
("2-1-2", "④ 가설전등"): None,
|
||||
("2-1-2", "⑦ 건설기계"): None,
|
||||
("2-12-2", "④"): 0,
|
||||
}
|
||||
|
||||
|
||||
def cells(line: str) -> list[str]:
|
||||
return [c.strip() for c in line.strip().strip("|").split("|")]
|
||||
|
||||
|
||||
def read_md(path: Path):
|
||||
text = path.read_text(encoding="utf-8")
|
||||
body = text.split("\n---\n", 1)[1]
|
||||
body = re.sub(r"<!--\s*/?글자층 밖\s*-->", "", body)
|
||||
body = re.sub(r"<!--.*?-->", "", body, flags=re.S)
|
||||
lines = body.split("\n")
|
||||
items, cur, mok, mok_no, block, skip_img = [], None, "", "", None, False
|
||||
for i, raw in enumerate(lines):
|
||||
line = raw.rstrip()
|
||||
m = re.match(r"^(#{2,4})\s+(.*)$", line)
|
||||
if m:
|
||||
block, skip_img = None, False
|
||||
if len(m.group(1)) == 3:
|
||||
idm = re.match(r"^(\d+-\d+-\d+)\s+(.*)$", m.group(2))
|
||||
cur = {
|
||||
"id": idm.group(1),
|
||||
"title": title_of(idm.group(2)),
|
||||
"tables": [],
|
||||
"lines": [],
|
||||
}
|
||||
items.append(cur)
|
||||
mok = mok_no = ""
|
||||
elif len(m.group(1)) == 4:
|
||||
mm = re.match(r"^(\d+)\.\s*(.*)$", m.group(2))
|
||||
mok, mok_no = title_of(mm.group(2)), mm.group(1)
|
||||
continue
|
||||
if cur is None:
|
||||
continue
|
||||
if not line.strip():
|
||||
block, skip_img = None, False
|
||||
continue
|
||||
if skip_img:
|
||||
continue
|
||||
if line.startswith("!["):
|
||||
skip_img = True
|
||||
continue
|
||||
if line.startswith("|"):
|
||||
cur["lines"].append(line)
|
||||
if i > 0 and lines[i - 1].startswith("|"):
|
||||
if not re.match(r"^\|[-|\s]+\|$", line):
|
||||
cur["tables"][-1].rows.append(cells(line))
|
||||
continue
|
||||
j = i - 1
|
||||
while j >= 0 and not lines[j].strip():
|
||||
j -= 1
|
||||
um = re.match(r"^\((.+)\)$", lines[j].strip()) if j >= 0 else None
|
||||
cur["tables"].append(
|
||||
Table(cur["id"], mok, mok_no, [cells(line)], um.group(1) if um else "")
|
||||
)
|
||||
continue
|
||||
cur["lines"].append(line)
|
||||
st = unesc(re.sub(r"^>\s?", "", line)).strip()
|
||||
if line.startswith("[주]"):
|
||||
firsts = [k for k, t in enumerate(cur["tables"]) if t.mok == mok]
|
||||
block = cur["tables"][firsts[0]]
|
||||
continue
|
||||
j = i + 1
|
||||
while j < len(lines) and not lines[j].strip():
|
||||
j += 1
|
||||
if re.match(r"^\(.+\)$", st) and j < len(lines) and lines[j].startswith("|"):
|
||||
continue # 밑수 줄
|
||||
if line.startswith("[참고자료]") or re.match(r"^<.+>$", line):
|
||||
continue # 표 제목 줄
|
||||
target = block or (cur["tables"][-1] if cur["tables"] else None)
|
||||
if target is None:
|
||||
continue
|
||||
for (item, head), k in ATTACH.items():
|
||||
if item == cur["id"] and st.startswith(head):
|
||||
target = None
|
||||
if k is not None:
|
||||
cur.setdefault("later", []).append((k, st))
|
||||
break
|
||||
if target is not None:
|
||||
target.notes.append(st)
|
||||
for cur in items:
|
||||
for k, st in cur.get("later", []):
|
||||
cur["tables"][k].notes.append(st)
|
||||
return items
|
||||
|
||||
|
||||
# ── 칸 읽기 ───────────────────────────────────────────────────────────
|
||||
def pv(c: str):
|
||||
"""칸 → (값, 단위, 원문) · 값 None = 빈 칸·「-」 · 글이면 str."""
|
||||
c = cl(c)
|
||||
if c in ("", "-"):
|
||||
return None, "", None
|
||||
m = re.fullmatch(rf"({NUM})\s*(%|〃)?", c)
|
||||
if m:
|
||||
return N(m.group(1)), "", None
|
||||
m = re.fullmatch(rf"([+-])\s*({NUM})\s*%", c)
|
||||
if m:
|
||||
return N(("-" if m.group(1) == "-" else "") + m.group(2)), "", c
|
||||
m = re.fullmatch(rf"({NUM})\s*[∼~]\s*({NUM})\s*([^\d\s]*)", c)
|
||||
if m:
|
||||
return [N(m.group(1)), N(m.group(2))], m.group(3), None
|
||||
m = re.fullmatch(rf"({NUM})\s*([^\d\s∼~%]+)", c)
|
||||
if m:
|
||||
return N(m.group(1)), m.group(2), None
|
||||
return c, "", None
|
||||
|
||||
|
||||
def prange(s: str):
|
||||
"""「1.5억 미만」 → [null, 1.5] · 「1.5 ∼ 3억」 → [1.5, 3] · 「500억 이상」 → [500, null]."""
|
||||
s = cl(s)
|
||||
m = re.fullmatch(rf"({NUM})\s*[∼~]\s*({NUM})\s*\S*", s)
|
||||
if m:
|
||||
return [N(m.group(1)), N(m.group(2))]
|
||||
m = re.fullmatch(rf"({NUM})\s*\S*\s*미만", s)
|
||||
if m:
|
||||
return [None, N(m.group(1))]
|
||||
m = re.fullmatch(rf"({NUM})\s*\S*\s*이상", s)
|
||||
if m:
|
||||
return [N(m.group(1)), None]
|
||||
raise ValueError(f"범위 아님 「{s}」")
|
||||
|
||||
|
||||
def spans(row, start=0):
|
||||
"""병합 머리 — 빈 칸은 왼쪽 칸 글을 이어 받음."""
|
||||
out, last = [], ""
|
||||
for k, c in enumerate(row):
|
||||
if k >= start and cl(c):
|
||||
last = cl(c)
|
||||
out.append(last if k >= start else "")
|
||||
return out
|
||||
|
||||
|
||||
def strip_head(s: str, head: str) -> str:
|
||||
return re.sub(rf"^{re.escape(head)}\s+", "", s)
|
||||
|
||||
|
||||
# ── 표 만들기 ─────────────────────────────────────────────────────────
|
||||
def grid(t, h, base, vals=(), texts=(), units=None, rule=None, pair=None, keep=(), cellunit=None):
|
||||
"""base=[(칸, 이름, 방식)] k=빈칸·〃는 위 값 · o=빈칸·「-」는 뺌 · u=단위(〃는 위 값) · r=범위 조건.
|
||||
vals=[(칸, 값이름, 조건(dict|함수|None), 홀로)] — 조건이 있으면 값마다 한 줄 · 홀로 = 위 base 를 붙이지 않음.
|
||||
texts=[(칸, 이름)] · 첫 칸이 「비고」 인 줄은 글."""
|
||||
conds, cols, out, last, plain = {}, {}, [], {}, {}
|
||||
ncol = max(len(r) for r in t.rows)
|
||||
for i, r in enumerate(t.rows[h:]):
|
||||
r = r + [""] * (ncol - len(r))
|
||||
if cl(r[0]) == "비고":
|
||||
out.append({"구분": "비고", "비고": next(cl(c) for c in r[1:] if cl(c))})
|
||||
conds["구분"], cols["비고"] = "고르기", "글"
|
||||
continue
|
||||
b = {}
|
||||
for c, key, mode in base:
|
||||
cell = lab(r[c])
|
||||
if mode == "r":
|
||||
b[key] = prange(cell)
|
||||
continue
|
||||
if mode in ("o", "u"):
|
||||
if cell == "〃":
|
||||
cell = last.get(key, "")
|
||||
elif cell in ("", "-"):
|
||||
continue
|
||||
else: # k
|
||||
if cell in ("", "〃"):
|
||||
cell = last[key]
|
||||
elif "〃" in cell:
|
||||
m = re.fullmatch(r"(\d+)\s*〃", cell)
|
||||
if m:
|
||||
cell = m.group(1) + re.sub(r"^[\d.]+", "", plain[key])
|
||||
else:
|
||||
cell = cell.replace("〃", re.split(r"[((]", plain[key])[0])
|
||||
else:
|
||||
plain[key] = cell
|
||||
if cell:
|
||||
last[key] = cell
|
||||
b[key] = cell
|
||||
for k, x in b.items():
|
||||
if k != "단위":
|
||||
conds[k] = "범위" if isinstance(x, list) else "고르기"
|
||||
pk = {"짝": pair[i]} if pair and pair[i] else {}
|
||||
if pk:
|
||||
conds["짝"] = "고르기"
|
||||
wide, longs = {}, []
|
||||
for c, key, cond, alone in vals:
|
||||
v, u, orig = pv(r[c])
|
||||
if v is None:
|
||||
continue
|
||||
if u and cellunit is None:
|
||||
raise ValueError(f"단위 버려짐 {t.item} {cl(r[c])}")
|
||||
cols.setdefault(key, "글" if isinstance(v, str) and not isinstance(v, Num) else None)
|
||||
if cond is None:
|
||||
wide[key] = v
|
||||
if orig:
|
||||
wide[key + "원문"] = orig
|
||||
if cellunit == "row" and u:
|
||||
wide["단위"] = u
|
||||
continue
|
||||
cond = cond(r) if callable(cond) else cond
|
||||
row = {k: b[k] for k in (keep if alone else b) if k in b}
|
||||
row.update(pk)
|
||||
row.update(cond)
|
||||
row[key] = v
|
||||
if orig:
|
||||
row[key + "원문"] = orig
|
||||
longs.append(row)
|
||||
for k, x in cond.items():
|
||||
conds[k] = "범위" if isinstance(x, list) else "고르기"
|
||||
for c, key in texts:
|
||||
if cl(r[c]):
|
||||
wide[key] = cl(r[c])
|
||||
cols[key] = "글"
|
||||
if wide:
|
||||
out.append({**b, **pk, **wide})
|
||||
out += longs
|
||||
has_unit = any("단위" in r for r in out)
|
||||
units = units or {}
|
||||
for k, v in cols.items():
|
||||
if v is None:
|
||||
m = re.search(r"\(([^)]+)\)$", k)
|
||||
cols[k] = units.get(k) or (m.group(1) if m else "줄 따름" if has_unit else "")
|
||||
conds = {k: v for k, v in conds.items() if any(k in r for r in out)}
|
||||
return {"조건": conds, "값칸": cols, "줄": out, "범위규칙": rule}
|
||||
|
||||
|
||||
def std(t, spec=None, unit=1):
|
||||
"""머리 한 단 [구분(,규격),단위,수량] — 값 칸 이름은 머리 글."""
|
||||
base = [(0, "구분", "k")] + ([(spec, "규격", "o")] if spec else []) + [(unit, "단위", "u")]
|
||||
vals = [(c, lab(t.rows[0][c]), None, False) for c in range(unit + 1, len(t.rows[0]))]
|
||||
return grid(t, 1, base, vals)
|
||||
|
||||
|
||||
def tiered(t, trow, conds, first, base, qty=None, key="수량", alone=False, keep=(), pair=None):
|
||||
"""머리 여러 단 — trow 줄부터 조건 이름 순서대로 머리 글(병합은 왼쪽 글 이어받음)이 조건."""
|
||||
ncol = max(len(r) for r in t.rows)
|
||||
tiers = [
|
||||
spans(t.rows[trow + k] + [""] * (ncol - len(t.rows[trow + k])), first)
|
||||
for k in range(len(conds))
|
||||
]
|
||||
vals = [(qty, "수량", None, False)] if qty is not None else []
|
||||
for c in range(first, ncol):
|
||||
if tiers[-1][c]:
|
||||
vals.append(
|
||||
(c, key, {cn: strip_head(tiers[k][c], cn) for k, cn in enumerate(conds)}, alone)
|
||||
)
|
||||
return grid(t, trow + len(conds), base, vals, keep=keep, pair=pair)
|
||||
|
||||
|
||||
def long1(t, spec, unit, first, cond, lrow, **kw):
|
||||
base = [(0, "구분", "k")] + ([(spec, "규격", "o")] if spec else []) + [(unit, "단위", "u")]
|
||||
return tiered(t, lrow, [cond], first, base, **kw)
|
||||
|
||||
|
||||
R_LABOR = (
|
||||
"「1.5억 미만」 = 위 끝 제외 · 「500억 이상」 = 아래 끝 포함 · 「1.5 ∼ 3억」 등 = 양 끝 포함"
|
||||
)
|
||||
FAC = ["현장사무소 감독・감리자", "현장사무소 수급자", "기자재창고", "숙소"]
|
||||
|
||||
|
||||
def office(t, n):
|
||||
vals = [(c + 1, "면적", {"시설": FAC[c]}, False) for c in range(n)]
|
||||
return grid(t, 2, [(0, "직접노무비(억)", "r")], vals, units={"면적": "㎡"}, rule=R_LABOR)
|
||||
|
||||
|
||||
def cement(t):
|
||||
rows = []
|
||||
for c, label in enumerate(t.rows[0]):
|
||||
parts = [cl(x) for x in t.rows[1][c].split("<br>")]
|
||||
sym = [p for p in parts[1:] if re.match(r"^[A-Za-z]=", p)]
|
||||
talk = [p for p in parts[1:] if not re.match(r"^[A-Za-z]=", p)]
|
||||
note = " · ".join(sym + ([" ".join(talk)] if talk else []))
|
||||
rows.append({"시설": cl(label), "식": parts[0], "설명": note})
|
||||
return {
|
||||
"조건": {"시설": "고르기"},
|
||||
"값칸": {"식": "글", "설명": "글"},
|
||||
"줄": rows,
|
||||
"범위규칙": None,
|
||||
}
|
||||
|
||||
|
||||
def area(t):
|
||||
g = grid(
|
||||
t,
|
||||
1,
|
||||
[(0, "종별", "k"), (1, "용도", "o")],
|
||||
[(2, "면적", None, False)],
|
||||
[(3, "비고")],
|
||||
cellunit="row",
|
||||
)
|
||||
for r in g["줄"]:
|
||||
if r["종별"] in ("콘크리트", "골재적치장"): # 표에서 한 이름으로 보임
|
||||
r["종별"] = "콘크리트 골재적치장"
|
||||
g["값칸"]["면적"] = "줄 따름"
|
||||
return g
|
||||
|
||||
|
||||
def periods(t, sub=False):
|
||||
"""머리 두 단 [기간 …] / [구분 …] — 기간이 조건."""
|
||||
labels = [lab(c) for c in t.rows[0]]
|
||||
first = 2 if sub else 1
|
||||
base = [(0, "구분", "k")] + ([(1, "종류", "o")] if sub else [])
|
||||
vals = [(c, "값", {"기간": labels[c]}, False) for c in range(first, len(labels))]
|
||||
return grid(t, 2, base, vals, units={"값": "%"})
|
||||
|
||||
|
||||
def rates(t, cond="설치간격", key="요율"):
|
||||
labels = [cl(c) for c in t.rows[0]]
|
||||
return grid(
|
||||
t,
|
||||
1,
|
||||
[],
|
||||
[(c, key, {cond: labels[c]}, False) for c in range(1, len(labels))],
|
||||
units={key: "%"},
|
||||
)
|
||||
|
||||
|
||||
def mast(t):
|
||||
labels = [cl(c) for c in t.rows[0]]
|
||||
return grid(
|
||||
t,
|
||||
1,
|
||||
[],
|
||||
[(c, "할증률", {"층수": labels[c]}, False) for c in range(len(labels))],
|
||||
units={"할증률": "%"},
|
||||
)
|
||||
|
||||
|
||||
def pairs_632(t):
|
||||
out, n, cur = [], 0, None
|
||||
for r in t.rows[2:]:
|
||||
r = r + [""] * 8
|
||||
if cl(r[0]) == "비고":
|
||||
out.append(None)
|
||||
continue
|
||||
if cl(r[0]):
|
||||
n = 0
|
||||
if any(cl(c) for c in r[4:8]):
|
||||
n += 1
|
||||
cur = "가" if n == 1 else "나"
|
||||
out.append(cur)
|
||||
return out
|
||||
|
||||
|
||||
def sys_dongbari(t):
|
||||
return tiered(
|
||||
t,
|
||||
1,
|
||||
["높이"],
|
||||
4,
|
||||
[(0, "작업", "k"), (1, "구분", "k"), (2, "단위", "u")],
|
||||
qty=3,
|
||||
key="시공량(공㎥)",
|
||||
alone=True,
|
||||
keep=("작업",),
|
||||
pair=pairs_632(t),
|
||||
)
|
||||
|
||||
|
||||
def rock(t):
|
||||
"""2-10-7 — 인력·장비 무리와 설치높이별 시공량 무리가 줄만 맞춰 나란히 찍힘 → 짝(그 줄 첫 칸 이름)."""
|
||||
names = [cl(r[0]) for r in t.rows[2:]]
|
||||
vals = [(2, "수량", None, False)] + [
|
||||
(c, "시공량(m)", (lambda w: lambda r: {"작업": w, "설치높이": cl(r[3])})(w), True)
|
||||
for c, w in ((4, "설치"), (5, "해체"))
|
||||
]
|
||||
return grid(t, 2, [(0, "구분", "k"), (1, "단위", "u")], vals, pair=names)
|
||||
|
||||
|
||||
def cap(t):
|
||||
rg = [prange(cl(c)) if cl(c) else None for c in t.rows[1]]
|
||||
vals = [(c, "작업능력", {"추진장(m)": rg[c]}, False) for c in range(2, len(rg))]
|
||||
return grid(
|
||||
t,
|
||||
2,
|
||||
[(0, "토질별", "k"), (1, "관경(㎜)", "r")],
|
||||
vals,
|
||||
units={"작업능력": "m/일"},
|
||||
rule="「300∼500」 등 양 끝 포함 · 추진장 「0∼10m」 · 「0∼20m」 등은 원문 그대로 겹친 누적 구간(양 끝 포함)",
|
||||
)
|
||||
|
||||
|
||||
def crew(t):
|
||||
rg = [prange(cl(c)) if cl(c) else None for c in t.rows[1]]
|
||||
vals = [(c, "수량", {"추진관경(㎜)": rg[c]}, False) for c in range(2, len(rg))]
|
||||
return grid(t, 2, [(0, "명칭", "k"), (1, "단위", "u")], vals, rule="「300∼600」 등 양 끝 포함")
|
||||
|
||||
|
||||
def C(fn, name=None, suffix=None, src=None):
|
||||
return {"fn": fn, "name": name, "suffix": suffix, "src": src}
|
||||
|
||||
|
||||
BASE_A = [(0, "구분", "k"), (1, "단위", "u")] # 구분 단위
|
||||
BASE_B = [(0, "구분", "k"), (1, "규격", "o"), (2, "단위", "u")] # 구분 규격 단위
|
||||
S3 = C(lambda t: std(t)) # 구분 단위 수량
|
||||
S4 = C(lambda t: std(t, spec=1, unit=2)) # 구분 규격 단위 수량
|
||||
REF = lambda name: C(S3["fn"], name, "재료량") # noqa: E731
|
||||
REF4 = lambda name: C(S4["fn"], name, "재료량") # noqa: E731
|
||||
SG = lambda key: C(lambda t: tiered(t, 1, ["높이"], 3, BASE_A, qty=2, key=key, alone=True)) # noqa: E731
|
||||
SG1 = lambda key: C(lambda t: grid(t, 1, BASE_A, [(2, "수량", None, False), (3, key, {}, True)])) # noqa: E731
|
||||
SG2 = lambda cs, first, spec: C(
|
||||
lambda t: tiered(
|
||||
t, 1, cs, first, BASE_B if spec else BASE_A, qty=first - 1, key="시공량(m)", alone=True
|
||||
)
|
||||
) # noqa: E731
|
||||
SG3 = lambda key: C(
|
||||
lambda t: tiered(
|
||||
t,
|
||||
1,
|
||||
["높이"],
|
||||
4,
|
||||
[(0, "작업", "k"), (1, "구분", "k"), (2, "단위", "u")],
|
||||
qty=3,
|
||||
key=key,
|
||||
alone=True,
|
||||
keep=("작업",),
|
||||
)
|
||||
) # noqa: E731
|
||||
LL = lambda spec, unit, first, cond, lrow: C(lambda t: long1(t, spec, unit, first, cond, lrow)) # noqa: E731
|
||||
RATE = lambda cond, name: C(lambda t: rates(t, cond), name, "요율") # noqa: E731
|
||||
LOSS = lambda name, fn=periods: C(fn, "가설재 손율 - " + name) # noqa: E731
|
||||
G2 = lambda col0, cond, rng, lrow=1: (
|
||||
lambda t: grid(
|
||||
t,
|
||||
2,
|
||||
[(0, col0, "k")],
|
||||
[(c, "손율", {cond: lab(t.rows[lrow][c])}, False) for c in rng],
|
||||
units={"손율": "%"},
|
||||
)
|
||||
) # noqa: E731
|
||||
LOOP = "파이프 루프공 - "
|
||||
|
||||
# fmt: off
|
||||
SPEC = {
|
||||
"2-1-1": [C(lambda t: office(t, 4))],
|
||||
"2-1-2": [
|
||||
C(lambda t: office(t, 3)),
|
||||
C(cement, "[참고자료] 시멘트 창고, 동력소 및 변전소 필요면적 산출", "시멘트 창고, 동력소 및 변전소 필요면적", "참고자료①"),
|
||||
C(area, "[참고자료] 가설물 면적", "가설물 면적", "참고자료②"),
|
||||
C(lambda t: grid(t, 1, [(0, "구분", "k"), (1, "자재종류", "o"), (2, "규격", "o"), (3, "단위", "u")],
|
||||
[(4, "수량", None, False), (5, "쌓기단수", None, False)], units={"수량": "줄 따름", "쌓기단수": "단"}),
|
||||
"[참고자료] 자재창고", "자재창고", "참고자료③"),
|
||||
C(lambda t: grid(t, 1, [(0, "구분", "k")], [(1, "수량", None, False)], [(2, "비고")], units={"수량": "등/㎡"}),
|
||||
"[참고자료] 가설전등", "가설전등", "참고자료④"),
|
||||
C(lambda t: grid(t, 1, [], [(0, "대당 소요면적", None, False)], [(1, "기준")], units={"대당 소요면적": "㎡"}, cellunit="col"),
|
||||
"[참고자료] 건설기계 주기장 산정", "건설기계 주기장 산정", "참고자료⑦"),
|
||||
],
|
||||
"2-2-2": [LOSS("주요자재", G2("구분", "사용기간별", range(1, 6), 0))],
|
||||
"2-2-3": [
|
||||
LOSS("철제조립식 가설건축물", lambda t: periods(t, sub=True)),
|
||||
C(lambda t: grid(t, 2, BASE_B, [(3, "수량", {"종류": "사무소"}, False), (4, "수량", {"종류": "창고"}, False)]),
|
||||
"[참고자료] 조립식 가설건축물의 주자재", "철제조립식 가설건축물 주자재", "1 참고자료"),
|
||||
LOSS("콘테이너형 가설건축물"),
|
||||
LOSS("가설울타리 및 가설방음벽", G2("사용시간", "재료", range(1, 4))),
|
||||
],
|
||||
"2-2-4": [LOSS("구조물 동바리")],
|
||||
"2-2-5": [LOSS("구조물 비계", G2("공기", "재료", range(1, 5)))],
|
||||
"2-2-6": [LOSS("축중계")],
|
||||
"2-2-7": [LOSS("규준틀", lambda t: grid(t, 1, [], [(1, "손율", {"규준틀": cl(t.rows[0][1])}, False), (2, "비고", {"규준틀": cl(t.rows[0][2])}, False)], units={"손율": "%"}))],
|
||||
"2-3-1": [LL(1, 2, 3, "종류", 0)],
|
||||
"2-3-2": [LL(1, 2, 3, "규모", 0)],
|
||||
"2-4-1": [SG2(["지주높이", "작업"], 4, True)],
|
||||
"2-4-2": [SG2(["지주높이", "작업"], 4, True)],
|
||||
"2-4-3": [SG2(["설치높이", "작업"], 3, False)],
|
||||
"2-4-4": [SG2(["설치높이", "작업"], 3, False)],
|
||||
"2-4-5": [SG2(["설치높이", "작업"], 4, True)],
|
||||
"2-5-1": [S3], "2-5-2": [S3],
|
||||
"2-5-3": [LL(None, 1, 2, "규준틀 높이", 1)],
|
||||
"2-5-4": [LL(None, 1, 2, "종별", 1)],
|
||||
"2-6-1": [SG("시공량(공㎥)"), C(S4["fn"], "수평연결재(1단 설치)", "수평연결재"), RATE("설치간격", "설치간격별 시공량 요율")],
|
||||
"2-6-2": [SG("시공량(㎡)"), C(S4["fn"], "수평연결재(1단 설치)", "수평연결재"), RATE("설치간격", "설치간격별 시공량 요율")],
|
||||
"2-6-3": [C(sys_dongbari), RATE("설치간격", "설치간격별 시공량 요율")],
|
||||
"2-6-4": [S3], "2-6-5": [S3],
|
||||
"2-7-1": [SG("수량(㎡)")],
|
||||
"2-7-2": [SG3("수량(㎡)")],
|
||||
"2-7-3": [SG3("수량(㎡)")],
|
||||
"2-7-4": [LL(1, 2, 3, "높이", 1)],
|
||||
"2-7-5": [S3],
|
||||
"2-7-6": [
|
||||
LL(1, 2, 3, "높이", 1),
|
||||
C(lambda t: grid(t, 1, BASE_B, [(3, "수량", None, False)], [(4, "비고")]), "[참고자료] 강관 조립말비계(이동식) 재료량", "재료량"),
|
||||
],
|
||||
"2-7-7": [S4], "2-7-8": [S4],
|
||||
"2-7-9": [C(lambda t: tiered(t, 1, ["부위", "작업"], 3, BASE_B))],
|
||||
"2-8-1": [SG1("시공량(㎡)"), REF4("[참고자료] 낙하물 방지망(비계) 재료량")],
|
||||
"2-8-2": [SG1("시공량(㎡)"), REF4("[참고자료] 낙하물 방지망(플라잉넷) 재료량")],
|
||||
"2-8-3": [SG1("시공량(㎡)")], "2-8-4": [SG1("시공량(㎡)")], "2-8-5": [SG1("시공량(㎡)")],
|
||||
"2-8-6": [LL(None, 1, 2, "출입구", 1)],
|
||||
"2-8-7": [S3, REF4("재료량")],
|
||||
"2-8-8": [S3, REF("재료량")], "2-8-9": [S3, REF("재료량")],
|
||||
"2-8-10": [LL(None, 1, 2, "개구부 면적", 1)],
|
||||
"2-8-11": [C(lambda t: tiered(t, 0, ["형식", "단수"], 2, BASE_A)), RATE("난간기둥 간격", "난간기둥 간격별 요율")],
|
||||
"2-8-12": [LL(None, 1, 2, "형식", 0)],
|
||||
"2-8-13": [LL(None, 1, 2, "단수", 0), RATE("난간기둥 간격", "난간기둥 간격별 요율")],
|
||||
"2-8-14": [S3], "2-8-15": [S3],
|
||||
"2-8-16": [LL(None, 1, 2, "개당 면적", 1)],
|
||||
"2-8-17": [S3], "2-8-18": [S3],
|
||||
"2-9-1": [S3], "2-9-2": [S3], "2-9-3": [S3], "2-9-4": [S3], "2-9-5": [S4], "2-9-6": [S4], "2-9-7": [S4],
|
||||
"2-9-8": [S3], "2-9-9": [S3], "2-9-10": [S3], "2-9-11": [S3], "2-9-12": [S3],
|
||||
"2-10-1": [S3, REF("재료량")], "2-10-2": [S3, REF("재료량")],
|
||||
"2-10-3": [LL(1, 2, 3, "작업", 1)],
|
||||
"2-10-4": [LL(1, 2, 3, "설치깊이", 1)],
|
||||
"2-10-5": [LL(1, 2, 3, "설치깊이", 1)],
|
||||
"2-10-6": [S3],
|
||||
"2-10-7": [C(rock)],
|
||||
"2-11-1": [LL(None, 1, 2, "작업", 0),
|
||||
C(lambda t: grid(t, 1, [(0, "구분", "k"), (1, "자재", "k"), (2, "단위", "u")], [(3, "수량", None, False)]), "재료량", "재료량")],
|
||||
"2-11-2": [LL(None, 1, 2, "구조", 0)],
|
||||
"2-11-3": [S3], "2-11-4": [S3], "2-11-5": [S4],
|
||||
"2-11-6": [LL(1, 2, 3, "작업", 1)],
|
||||
"2-11-7": [S4],
|
||||
"2-11-8": [LL(1, 2, 3, "설치장소", 1)],
|
||||
"2-11-9": [S4],
|
||||
"2-12-1": [S4],
|
||||
"2-12-2": [S3, C(mast, "마스트 높이별 할증률", "높이별 할증률")],
|
||||
"2-12-3": [S3],
|
||||
"2-12-4": [
|
||||
C(lambda t: grid(t, 1, [(0, "구분", "k"), (1, "명칭", "k"), (2, "규격", "o"), (3, "단위", "u")], [(4, "수량", None, False)], [(5, "비고")]), LOOP + "장비 조립해체"),
|
||||
C(crew, LOOP + "작업편성인원"),
|
||||
C(lambda t: grid(t, 1, [(0, "장비명", "k"), (1, "규격", "o"), (2, "단위", "u")], [(3, "수량", None, False)], [(4, "비고")]), LOOP + "작업편성장비"),
|
||||
C(cap, LOOP + "작업능력"),
|
||||
C(lambda t: grid(t, 1, [(0, "이동구분", "k"), (1, "이동용 장비", "k")], [(2, "소요시간", None, False)], [(3, "비고")], units={"소요시간": "분"}), LOOP + "기계이동 설치"),
|
||||
],
|
||||
}
|
||||
# fmt: on
|
||||
|
||||
|
||||
# ── 만들기 ────────────────────────────────────────────────────────────
|
||||
def build():
|
||||
tables, count = [], {}
|
||||
for path in sorted(SRC.glob("2-*.md")):
|
||||
items = read_md(path)
|
||||
count[path.name] = [0, 0, 0]
|
||||
for item in items:
|
||||
specs = SPEC.get(item["id"])
|
||||
got = item["tables"]
|
||||
if not got and specs is None:
|
||||
continue # 표 없는 글뿐인 항(2-2-1) — 계산용 수 없음
|
||||
if specs is None or len(specs) != len(got):
|
||||
raise SystemExit(
|
||||
f"{item['id']} 표 {len(got)} · 매핑 {0 if specs is None else len(specs)}"
|
||||
)
|
||||
for k, (t, sp) in enumerate(zip(got, specs)):
|
||||
g = sp["fn"](t)
|
||||
name = sp["name"] or (t.mok or item["title"])
|
||||
key = f"{item['id']} " + (sp["suffix"] or t.mok or item["title"])
|
||||
src = sp["src"] or (
|
||||
t.mok_no + (" 참고자료" if sp["suffix"] and t.mok else "") if t.mok else ""
|
||||
)
|
||||
tb = {
|
||||
"열쇠": key,
|
||||
"이름": name,
|
||||
"기준": t.base,
|
||||
"출처": f"{BOOK} {item['id']}" + (f" {src}" if src else ""),
|
||||
}
|
||||
tb["조건"], tb["값칸"] = g["조건"], g["값칸"]
|
||||
if g["범위규칙"]:
|
||||
tb["범위규칙"] = g["범위규칙"]
|
||||
tb["줄"], tb["주"] = g["줄"], t.notes
|
||||
tables.append(tb)
|
||||
count[path.name][0] += 1
|
||||
lt = lift_table(item, item["title"], unesc, N, BOOK)
|
||||
if lt:
|
||||
tables.append(lt)
|
||||
count[path.name][1] += 1
|
||||
return tables, count
|
||||
|
||||
|
||||
# ── 찍기 ──────────────────────────────────────────────────────────────
|
||||
def js(x):
|
||||
if isinstance(x, Num):
|
||||
return str(x)
|
||||
if x is None:
|
||||
return "null"
|
||||
if isinstance(x, list):
|
||||
return "[" + ", ".join(js(v) for v in x) + "]"
|
||||
if isinstance(x, dict):
|
||||
return (
|
||||
"{ "
|
||||
+ ", ".join(f"{json.dumps(k, ensure_ascii=False)}: {js(v)}" for k, v in x.items())
|
||||
+ " }"
|
||||
if x
|
||||
else "{}"
|
||||
)
|
||||
return json.dumps(x, ensure_ascii=False)
|
||||
|
||||
|
||||
def dump(tables):
|
||||
out = ['{\n "그룹": "소요량",\n "원문": "건설품셈",\n "판": "2026",\n "표": [']
|
||||
blocks = []
|
||||
for tb in tables:
|
||||
ln = [" {"]
|
||||
fields = []
|
||||
for k, v in tb.items():
|
||||
if k == "줄":
|
||||
inner = ",\n".join(" " + js(r) for r in v)
|
||||
fields.append(f' "줄": [\n{inner}\n ]')
|
||||
elif k == "주":
|
||||
inner = ",\n".join(" " + json.dumps(s, ensure_ascii=False) for s in v)
|
||||
fields.append(f' "주": [\n{inner}\n ]' if v else ' "주": []')
|
||||
else:
|
||||
fields.append(f" {json.dumps(k, ensure_ascii=False)}: {js(v)}")
|
||||
ln.append(",\n".join(fields))
|
||||
ln.append(" }")
|
||||
blocks.append("\n".join(ln))
|
||||
out.append("\n" + ",\n".join(blocks) + "\n ]\n}\n")
|
||||
return "".join(out)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.stdout.reconfigure(encoding="utf-8")
|
||||
tables, count = build()
|
||||
keys = [t["열쇠"] for t in tables]
|
||||
dup = {k for k in keys if keys.count(k) > 1}
|
||||
if dup:
|
||||
raise SystemExit(f"열쇠 겹침 {dup}")
|
||||
OUT.write_text(dump(tables), encoding="utf-8", newline="\n")
|
||||
for f, (a, b, _) in count.items():
|
||||
print(f"{f} · 옮긴 표 {a} · 계수 표 {b}")
|
||||
print("합계", sum(a for a, _, _ in count.values()), sum(b for _, b, _ in count.values()))
|
||||
@@ -0,0 +1,609 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# ruff: noqa: E501
|
||||
"""건설품셈 공통 제3장(토공사) 본문 md -> resources/master_data/소요량_건설품셈_공통3장.json
|
||||
|
||||
값은 md 표·글에서 읽음(손 타이핑 없음). 표 하나 = 표 하나 · 글 속 계산용 수 = 계수 표.
|
||||
돌리기: ./venv/Scripts/python.exe resources/master_data/scripts/build_소요량_건설품셈_공통3장.py
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
||||
|
||||
from _md표_읽기 import ( # noqa: E402
|
||||
LOSE,
|
||||
PCT,
|
||||
TABLES,
|
||||
TABS,
|
||||
N,
|
||||
R,
|
||||
across,
|
||||
add,
|
||||
bigo,
|
||||
coef,
|
||||
crew,
|
||||
grouped,
|
||||
join,
|
||||
notes,
|
||||
one,
|
||||
rate,
|
||||
rng,
|
||||
sec_text,
|
||||
sg_unit,
|
||||
sp,
|
||||
title,
|
||||
write,
|
||||
)
|
||||
|
||||
|
||||
# ── 표 정의 ────────────────────────────────────────────────
|
||||
def b_굴착():
|
||||
T = TABS["3-02"]
|
||||
t = T[0]
|
||||
cols, rows = across(t, "토질", 1, 2, 2, 3, 4, spec=None, unit=1, qty=2)
|
||||
add(t, "3-2-1", None, {"토질": "고르기"}, cols, rows, bigo(t, 3, 1) + notes("3-2-1"))
|
||||
coef("3-2-1", "굴착(인력/토사) 계수", "비고 · 주①④", [
|
||||
("잔토처리 보통인부", r"보통인부 ([\d.]+)인", "인/㎥"),
|
||||
("넓은 구역 터파기 시공량 가산 한도", r"시공량을 ([\d.]+)%까지 가산", "%"),
|
||||
("적용 깊이 한도", r"깊이 ([\d.]+)m이하", "m"),
|
||||
("용수 시공량 감 한도", r"시공량의 ([\d.]+)% 범위 내에서 감하여", "%"),
|
||||
]) # fmt: skip
|
||||
t = T[1]
|
||||
cols = {}
|
||||
for c in range(1, 5):
|
||||
m = re.match(r"(.*?)<br>\((.*)\)", t.rows[0][c])
|
||||
cols[m.group(1)] = m.group(2)
|
||||
rows = [
|
||||
{"암질": t.rows[r][0], **{k: N(t.rows[r][1 + i]) for i, k in enumerate(cols)}}
|
||||
for r in range(2, 6)
|
||||
]
|
||||
note = ["비고 " + join(t.rows[2][5])] + notes("3-2-2")
|
||||
add(t, "3-2-2", None, {"암질": "고르기"}, cols, rows, note)
|
||||
coef("3-2-2", "굴착(인력/암반) 계수", "주②④", [
|
||||
("굴착토량 단위개소당 미만 적용", r"단위개소당 ([\d,]+)㎥미만", "㎥"),
|
||||
("잡재료 인력품 비율", r"잡재료는 인력품의 ([\d.]+)%", "%"),
|
||||
]) # fmt: skip
|
||||
# 3-2-3
|
||||
t = T[2]
|
||||
rows = []
|
||||
for k, cell in enumerate(t.rows[1]):
|
||||
txt = join(cell)
|
||||
col = "공사수량이상" if "이상" in txt else "공사수량미만"
|
||||
rows.append(
|
||||
{
|
||||
"규모": t.rows[0][k],
|
||||
col: N(re.search(r"([\d,]+)㎥", txt).group(1)),
|
||||
col + "원문": txt,
|
||||
}
|
||||
)
|
||||
cols = {
|
||||
"공사수량이상": "㎥",
|
||||
"공사수량이상원문": "글",
|
||||
"공사수량미만": "㎥",
|
||||
"공사수량미만원문": "글",
|
||||
}
|
||||
add(
|
||||
None,
|
||||
"3-2-3",
|
||||
"흙깎기(기계) 공사규모 구분",
|
||||
{"규모": "고르기"},
|
||||
cols,
|
||||
rows,
|
||||
[join(t.rows[2][0])],
|
||||
group="계수",
|
||||
)
|
||||
for i, nmx in ((3, "보통토사"), (4, "혼합토사")):
|
||||
cols, rows = grouped(T[i], "규모", 2, 4)
|
||||
note = notes("3-2-3", 0) if i == 4 else []
|
||||
add(T[i], "3-2-3", f"흙깎기(기계) {nmx}", {"규모": "고르기"}, cols, rows, note)
|
||||
cols, rows = across(T[5], "암분류", 1, 2, 3, 4, 4)
|
||||
add(T[5], "3-2-3", "흙깎기(기계) 암", {"암분류": "고르기"}, cols, rows, notes("3-2-3", 1))
|
||||
cols, rows = across(T[6], "암분류", 1, 2, 2, 4, 4)
|
||||
add(T[6], "3-2-3", "흙깎기(기계) 암 집토", {"암분류": "고르기"}, cols, rows, src="3-2-3 주①")
|
||||
cols, rows = rate(T[7], "암분류")
|
||||
add(
|
||||
None,
|
||||
"3-2-3",
|
||||
"흙깎기(기계) 암 소모재료(치즐) 요율",
|
||||
{"암분류": "고르기"},
|
||||
cols,
|
||||
rows,
|
||||
src="3-2-3 주②",
|
||||
group="계수",
|
||||
)
|
||||
coef("3-2-3", "흙깎기(기계) 계수", "주②", [
|
||||
("유압식 리퍼 경비 비율", r"리퍼의 경비는 깎기장비 기계경비\(재료비, 노무비, 경비\)의 " + PCT, "%")
|
||||
]) # fmt: skip
|
||||
# 3-2-4
|
||||
t = T[8]
|
||||
rows = [{"Type": r[0], "적용기준": r[1]} for r in t.rows[1:4]]
|
||||
add(
|
||||
None,
|
||||
"3-2-4",
|
||||
"터파기(기계) 규격 구분",
|
||||
{"Type": "고르기"},
|
||||
{"적용기준": "글"},
|
||||
rows,
|
||||
[join(t.rows[4][0])],
|
||||
)
|
||||
for i, nmx in ((9, "보통토사"), (10, "혼합토사")):
|
||||
t = T[i]
|
||||
cols, rows = grouped(t, "Type", 2, 3)
|
||||
note = bigo(t, 4, 1) + (notes("3-2-4", 0) if i == 10 else [])
|
||||
add(t, "3-2-4", f"터파기(기계) {nmx}", {"Type": "고르기"}, cols, rows, note)
|
||||
t = T[11]
|
||||
cols = {"굴착기": "대", "굴착기규격": "글", "대형브레이커": "대", "대형브레이커규격": "글"}
|
||||
cols["시공량"] = sg_unit(t)
|
||||
rows = []
|
||||
for ty in range(2):
|
||||
sps = [x.strip() for x in t.rows[2][5 + ty].split("<br>")]
|
||||
for i, g in enumerate(t.rows[2][4].split("<br>")):
|
||||
rows.append({
|
||||
"Type": t.rows[1][5 + ty], "암분류": g,
|
||||
"굴착기": N(t.rows[2][3]), "굴착기규격": t.rows[2][1],
|
||||
"대형브레이커": N(t.rows[3][3]), "대형브레이커규격": t.rows[3][1], "시공량": N(sps[i]),
|
||||
}) # fmt: skip
|
||||
note = bigo(t, 4, 1) + notes("3-2-4", 1)
|
||||
add(t, "3-2-4", "터파기(기계) 암", {"Type": "고르기", "암분류": "고르기"}, cols, rows, note)
|
||||
cols, rows = rate(T[12], "암분류")
|
||||
add(
|
||||
None,
|
||||
"3-2-4",
|
||||
"터파기(기계) 암 소모재료(치즐) 요율",
|
||||
{"암분류": "고르기"},
|
||||
cols,
|
||||
rows,
|
||||
src="3-2-4 주",
|
||||
group="계수",
|
||||
)
|
||||
coef("3-2-4", "터파기(기계) 비고 계수", "비고", [
|
||||
("용수 발생 시공량 감", r"용수 발생으로 인해 터파기 작업에 지장이 발생하는 경우 시공량을 ([\d.]+)% 감", "%"),
|
||||
("굴착 깊이 기준 초과", r"굴착 깊이가 ([\d.]+)m를 초과", "m"),
|
||||
("굴착 깊이 초과 시공량 감", r"m를 초과하는 경우 시공량을 ([\d.]+)% 감", "%"),
|
||||
]) # fmt: skip
|
||||
coef("3-2-4", "터파기(기계) 주 계수", "주②", [
|
||||
("유압식 리퍼 경비 비율", r"터파기장비 기계경비\(재료비, 노무비, 경비\)의 " + PCT, "%")
|
||||
]) # fmt: skip
|
||||
|
||||
|
||||
def b_발파():
|
||||
T = TABS["3-03"]
|
||||
for i in range(6):
|
||||
ident = f"3-3-{i + 1}"
|
||||
cols, rows = one(T[i], 1, 4)
|
||||
add(T[i], ident, None, {}, cols, rows, notes("3-3-6") if i == 5 else [])
|
||||
cols, rows = one(T[6], 1, 2)
|
||||
add(T[6], "3-3-6", "발파보호공", {}, cols, rows, src="3-3-6 주⑥")
|
||||
cols, rows = rate(T[7], "장비")
|
||||
add(
|
||||
None,
|
||||
"3-3-6",
|
||||
"소모자재 요율",
|
||||
{"장비": "고르기"},
|
||||
cols,
|
||||
rows,
|
||||
src="3-3-6 주⑨",
|
||||
group="계수",
|
||||
)
|
||||
t = T[8]
|
||||
rows = [
|
||||
{
|
||||
"장비": join(t.rows[2][0]) + " " + t.rows[2][1],
|
||||
"파쇄크기": t.rows[1][c],
|
||||
"작업능력": N(t.rows[2][c]),
|
||||
}
|
||||
for c in (2, 3)
|
||||
]
|
||||
add(
|
||||
None,
|
||||
"3-3-6",
|
||||
"기계소할 작업능력",
|
||||
{"장비": "고르기", "파쇄크기": "고르기"},
|
||||
{"작업능력": "㎥/hr"},
|
||||
rows,
|
||||
src="3-3-6 주⑩",
|
||||
)
|
||||
coef("3-3-6", "암발파 계수", "주⑦⑩", [
|
||||
("잡재료 재료비 비율", r"전색재료 등의 잡재료는 재료비의 ([\d.]+)%", "%"),
|
||||
("소할물량 유용량 비율", r"소할물량은 유용량의 ([\d.]+)%", "%"),
|
||||
]) # fmt: skip
|
||||
cols, rows = one(T[9], 1, 8)
|
||||
add(T[9], "3-3-7", None, {}, cols, rows, notes("3-3-7"))
|
||||
coef("3-3-7", "암발파(소형브레이커) 계수", "주①②", [
|
||||
("적용 절취폭 미만", r"절취폭이 ([\d.]+)m 미만", "m"),
|
||||
("터파기 시 재료비 제외 품 가산", r"품의 ([\d.]+)%를 가산", "%"),
|
||||
]) # fmt: skip
|
||||
cols, rows = one(T[10], 1, 6)
|
||||
add(T[10], "3-3-8", None, {}, cols, rows, notes("3-3-8"))
|
||||
cols, rows = rate(T[11], "장비")
|
||||
add(
|
||||
None,
|
||||
"3-3-8",
|
||||
"소모자재 요율",
|
||||
{"장비": "고르기"},
|
||||
cols,
|
||||
rows,
|
||||
src="3-3-8 주④",
|
||||
group="계수",
|
||||
)
|
||||
t = T[12]
|
||||
cols = {}
|
||||
rows = [{"발파": join(t.rows[1][c])} for c in (3, 4)]
|
||||
for r in range(2, 11):
|
||||
row = t.rows[r]
|
||||
n = join(row[0])
|
||||
cols[n] = row[2]
|
||||
if sp(row):
|
||||
cols[n + "규격"] = "글"
|
||||
for k, c in enumerate((3, 4)):
|
||||
if sp(row):
|
||||
rows[k][n + "규격"] = sp(row)
|
||||
m = re.fullmatch(r"([\d.]+)(\(.*\))", row[c])
|
||||
if m:
|
||||
rows[k][n], rows[k][n + "원문"] = R(m.group(1)), row[c]
|
||||
cols[n + "원문"] = "글"
|
||||
else:
|
||||
rows[k][n] = N(row[c])
|
||||
add(t, "3-3-9", None, {"발파": "고르기"}, cols, rows, notes("3-3-9"))
|
||||
txt = sec_text("3-3-9")
|
||||
rx = r"수심 [\d.]+m이상~[\d.]+m미만을 기준한 것으로, 수심 ([\d.]+)m미만에서는 .*?의 ([\d.]+)%를 감할 수 있으며, 수심이 ([\d.]+)m이상~" # noqa: E501
|
||||
lo_lim, lo_pct, hi_from = (R(x) for x in re.search(rx, txt).groups())
|
||||
rx = r"수심이 [\d.]+m이상~([\d.]+)m미만에서는 .*?의 ([\d.]+)%를 가산"
|
||||
hi_to, hi_pct = (R(x) for x in re.search(rx, txt).groups())
|
||||
rows = [{"수심": [None, lo_lim], "감": lo_pct}, {"수심": [hi_from, hi_to], "가산": hi_pct}]
|
||||
add(None, "3-3-9", "수중발파 수심별 품 증감", {"수심": "범위"}, {"감": "%", "가산": "%"}, rows,
|
||||
src="3-3-9 주②", base="재료비(폭약, 뇌관) 제외 품", group="계수", rule="이상·미만 · 수심 m") # fmt: skip
|
||||
|
||||
|
||||
def b_쌓기():
|
||||
T = TABS["3-04"]
|
||||
t = T[0]
|
||||
rows = [
|
||||
{
|
||||
"두께": t.rows[0][c],
|
||||
"다짐도": R(re.match(r"[\d.]+", t.rows[1][c]).group(0)),
|
||||
"다짐도원문": t.rows[1][c],
|
||||
}
|
||||
for c in (1, 2)
|
||||
]
|
||||
add(
|
||||
None,
|
||||
"3-4-1",
|
||||
"흙쌓기 두께별 다짐도",
|
||||
{"두께": "고르기"},
|
||||
{"다짐도": "%", "다짐도원문": "글"},
|
||||
rows,
|
||||
group="계수",
|
||||
)
|
||||
cols, rows = across(T[1], "두께", 1, 2, 6, 4, 2)
|
||||
add(T[1], "3-4-1", "흙쌓기(다짐)", {"두께": "고르기"}, cols, rows)
|
||||
cols, rows = one(T[2], 1, 1, sg=4)
|
||||
add(T[2], "3-4-1", "흙쌓기(비다짐)", {}, cols, rows, notes("3-4-1"))
|
||||
cols, rows = one(T[3], 1, 4, sg=4)
|
||||
add(T[3], "3-4-2", None, {}, cols, rows, notes("3-4-2"))
|
||||
coef("3-4-2", "암쌓기 계수", "주①", [("다짐두께", r"다짐두께는 ([\d.]+)㎝", "㎝")])
|
||||
t = T[4]
|
||||
cols, rows = {}, []
|
||||
for r0, r1 in ((2, 4), (5, 7)):
|
||||
c, v = crew(t, r0, r1)
|
||||
cols.update(c)
|
||||
for ti, tn in enumerate(t.rows[1][5:7]):
|
||||
vals5 = t.rows[r0][5 + ti].split("<br>")
|
||||
for k, th in enumerate(t.rows[r0][4].split("<br>")):
|
||||
d = {
|
||||
"짝": join(t.rows[r1][0]),
|
||||
"다짐두께": R(re.match(r"\d+", th).group(0)),
|
||||
"토질": tn,
|
||||
}
|
||||
rows.append({**d, **v, "시공량": N(vals5[k])})
|
||||
cols["시공량"] = sg_unit(t)
|
||||
add(
|
||||
t,
|
||||
"3-4-3",
|
||||
None,
|
||||
{"짝": "고르기", "다짐두께": "수", "토질": "고르기"},
|
||||
cols,
|
||||
rows,
|
||||
notes("3-4-3"),
|
||||
)
|
||||
for i in (5, 6, 7, 8):
|
||||
ident = f"3-4-{i - 1}"
|
||||
cols, rows = one(T[i], 1, len(T[i].rows) - 1, sg=4)
|
||||
add(T[i], ident, None, {}, cols, rows, notes(ident))
|
||||
cols, rows = grouped(T[9], "지정", 2, 6)
|
||||
add(T[9], "3-4-8", None, {"지정": "고르기"}, cols, rows, notes("3-4-8"))
|
||||
|
||||
|
||||
def b_부대공():
|
||||
T = TABS["3-05"]
|
||||
t = T[0]
|
||||
cols, rows = {}, []
|
||||
for r in (1, 2, 3):
|
||||
c, v = crew(t, r, r)
|
||||
cols.update(c)
|
||||
for so, sv in zip(t.rows[r][4].split("<br>"), t.rows[r][5].split("<br>")):
|
||||
rows.append({t.rows[0][4]: so, **v, "시공량": N(sv)})
|
||||
cols["시공량"] = sg_unit(t)
|
||||
add(t, "3-5-1", None, {t.rows[0][4]: "고르기"}, cols, rows, notes("3-5-1"))
|
||||
coef(
|
||||
"3-5-1",
|
||||
"절토면 고르기 계수",
|
||||
"주②",
|
||||
[("리퍼 기계경비 굴착기 비율", r"굴착기 기계경비의 ([\d.]+)%", "%")],
|
||||
)
|
||||
cols, rows = across(T[1], "대상", 1, 2, 4, 4, 2)
|
||||
add(T[1], "3-5-2", None, {"대상": "고르기"}, cols, rows, notes("3-5-2"))
|
||||
coef("3-5-2", "암반청소 계수", "주④", [LOSE])
|
||||
T = TABS["3-06"]
|
||||
cols, rows = one(T[0], 1, 1, sg=4)
|
||||
add(T[0], "3-6-1", None, {}, cols, rows, notes("3-6-1"))
|
||||
cols, rows = one(T[1], 1, 2, sg=3, spec=None, unit=1, qty=2)
|
||||
add(T[1], "3-6-2", None, {}, cols, rows, notes("3-6-2"))
|
||||
|
||||
|
||||
def b_비탈면():
|
||||
T = TABS["3-07"]
|
||||
t = T[0]
|
||||
cols, rows = {}, []
|
||||
for lab, (r0, r1) in (("인력", (2, 3)), ("기계", (4, 6))):
|
||||
c, v = crew(t, r0, r1, name_c=1, spec=None)
|
||||
cols.update(c)
|
||||
for k in range(3):
|
||||
slope = re.sub(r"^비탈경사\s*", "", join(t.rows[1][4 + k]))
|
||||
rows.append({"시공방법": lab, "비탈경사": slope, **v, "시공량": N(t.rows[r0][4 + k])})
|
||||
cols["시공량"] = sg_unit(t)
|
||||
note = [f"비고 {t.rows[7][2]}"] + notes("3-7-1")
|
||||
add(t, "3-7-1", None, {"시공방법": "고르기", "비탈경사": "고르기"}, cols, rows, note)
|
||||
coef("3-7-1", "프리캐스트 콘크리트 블록설치 계수", "비고 · 주①②③", [
|
||||
("유항 설치 보통인부", r"보통인부 ([\d.]+)인/10본당", "인/10본"),
|
||||
("시공범위 수직고 이하", r"수직고 ([\d.]+)m이하", "m"),
|
||||
("인력 적용 블록중량 미만", r"블록중량이 ([\d.]+)㎏/개 미만으로서", "㎏/개"),
|
||||
("인력 적용 평균 비탈길이 이하", r"평균 비탈길이가 ([\d.]+)m이하", "m"),
|
||||
("기계 적용 블록중량 이상", r"블록중량이 ([\d.]+)㎏/개 이상인", "㎏/개"),
|
||||
("기계 적용 평균 비탈길이 초과", r"평균 비탈길이가 ([\d.]+)m를 초과", "m"),
|
||||
]) # fmt: skip
|
||||
cols, rows = one(T[1], 1, 7, sg=4)
|
||||
add(T[1], "3-7-2", None, {}, cols, rows, notes("3-7-2"))
|
||||
coef("3-7-2", "지압판블록 설치 계수", "주①②⑤", [
|
||||
("블록중량 이하", r"블록\(([\d.]+)ton이하\)", "ton"),
|
||||
("적용 비탈경사 이하", r"비탈경사 1:([\d.]+)이하", "", r"1:[\d.]+이하"),
|
||||
("수직고 한도", r"수직고 ([\d.]+)m까지", "m"),
|
||||
LOSE,
|
||||
]) # fmt: skip
|
||||
cols, rows = one(T[2], 1, 2, sg=3, spec=None, unit=1, qty=2)
|
||||
add(T[2], "3-7-3", None, {}, cols, rows, notes("3-7-3"))
|
||||
coef("3-7-3", "천연섬유사면보호공 설치 계수", "주①②", [
|
||||
("적용 비탈경사 범위", r"비탈경사 1:([\d.]+)~([\d.]+)\)", "", r"비탈경사 1:[\d.]+~[\d.]+"),
|
||||
("높이 기준", r"높이 ([\d.]+)m 기준", "m"),
|
||||
]) # fmt: skip
|
||||
# 3-7-4
|
||||
t = T[3]
|
||||
cols, vals = crew(t, 1, 3, spec=None, unit=1, qty=2)
|
||||
cols["시공량"] = sg_unit(t)
|
||||
rows = [
|
||||
{"뿜어붙이기 두께": th, **vals, "시공량": N(sv)}
|
||||
for th, sv in zip(t.rows[1][3].split("<br>"), t.rows[1][4].split("<br>"))
|
||||
]
|
||||
note = [f"비고 {t.rows[4][1]}"] + notes("3-7-4", 0)
|
||||
add(t, "3-7-4", "부착망 설치", {"뿜어붙이기 두께": "고르기"}, cols, rows, note)
|
||||
for i, ky in ((4, "부착망 설치"), (8, "뿜어붙이기")):
|
||||
t = T[i]
|
||||
rows = [{"수직고": t.rows[0][c], "할증률": N(t.rows[1][c])} for c in (1, 2, 3)]
|
||||
add(None, "3-7-4", f"{ky} 수직고별 할증률", {"수직고": "고르기"}, {"할증률": "%"}, rows,
|
||||
src="3-7-4 비고", base="비고 칸 안 표(수직고 20m 이상 시공량 감)", group="계수") # fmt: skip
|
||||
t = T[5]
|
||||
cols, names = {}, []
|
||||
for c in range(1, 5):
|
||||
names.append(t.rows[0][c].split("(")[0])
|
||||
cols[names[-1]] = re.search(r"\((.*)\)", t.rows[0][c]).group(1)
|
||||
cols[names[-1] + "규격"] = "글"
|
||||
rows = []
|
||||
for r in (2, 3):
|
||||
d = {"두께": t.rows[r][0]}
|
||||
for c, n in zip(range(1, 5), names):
|
||||
d[n], d[n + "규격"] = N(t.rows[r][c]), t.rows[1][c]
|
||||
rows.append(d)
|
||||
add(t, "3-7-4", "부착망 설치 재료량(참고자료)", {"두께": "고르기"}, cols, rows,
|
||||
["※ 재료 할증량은 포함되어 있다."], src="3-7-4 참고자료") # fmt: skip
|
||||
cols, rows = one(T[6], 1, 3)
|
||||
add(T[6], "3-7-4", "기계기구 설치 및 해체", {}, cols, rows, notes("3-7-4", 1))
|
||||
t = T[7]
|
||||
cols, vals = crew(t, 1, 9)
|
||||
cols["시공량"] = sg_unit(t)
|
||||
rows = [
|
||||
{"뿜어붙이기 두께": th, **vals, "시공량": N(sv)}
|
||||
for th, sv in zip(t.rows[1][4].split("<br>"), t.rows[1][5].split("<br>"))
|
||||
]
|
||||
note = [f"비고 {t.rows[10][1]}"] + notes("3-7-4", 2)
|
||||
add(t, "3-7-4", "뿜어붙이기", {"뿜어붙이기 두께": "고르기"}, cols, rows, note)
|
||||
coef("3-7-4", "부착망 설치 계수", "주⑤⑥", [
|
||||
("공구손료 및 경장비 인력품 비율", r"소형천공기, 발전기 등\)는 인력품의 ([\d.]+)%", "%"),
|
||||
("잡재료비 재료비 비율", r"잡재료비는 재료비의 ([\d.]+)%", "%"),
|
||||
]) # fmt: skip
|
||||
coef("3-7-4", "뿜어붙이기 계수", "주③④", [
|
||||
("공구손료 및 경장비 인력품 비율", r"\(발전기 등\)는 인력품의 ([\d.]+)%", "%"),
|
||||
("잡재료비 재료비 비율", r"각 공법의 설계기준에 따라 계상하며, 잡재료비는 재료비의 ([\d.]+)%", "%"),
|
||||
]) # fmt: skip
|
||||
# 3-7-5
|
||||
cols, rows = one(T[9], 1, 3)
|
||||
add(T[9], "3-7-5", "장비 조립・해체", {}, cols, rows, notes("3-7-5", 0))
|
||||
cols, rows = one(T[10], 1, 6)
|
||||
add(T[10], "3-7-5", "인력 및 장비 편성", {}, cols, rows, notes("3-7-5", 1))
|
||||
t = T[11]
|
||||
rows = [
|
||||
{"작업": t.rows[r][0], "지반": g, "시공량": N(t.rows[r][1 + k])}
|
||||
for r in (2, 3)
|
||||
for k, g in enumerate(t.rows[1][1:])
|
||||
]
|
||||
cols = {"시공량": re.search(r"\((.*)\)", t.rows[0][1]).group(1)}
|
||||
add(
|
||||
t,
|
||||
"3-7-5",
|
||||
"일당시공량",
|
||||
{"작업": "고르기", "지반": "고르기"},
|
||||
cols,
|
||||
rows,
|
||||
notes("3-7-5", 2),
|
||||
)
|
||||
cols, rows = one(T[12], 1, 2, sg=3, spec=None, unit=1, qty=2)
|
||||
add(T[12], "3-7-5", "보강재 조립", {}, cols, rows, src="3-7-5 주⑤")
|
||||
cols, rows = one(T[13], 1, 6, sg=4)
|
||||
add(T[13], "3-7-5", "그라우팅", {}, cols, rows, notes("3-7-5", 3))
|
||||
coef("3-7-5", "비탈면 보강공 계수", "주", [
|
||||
("일당시공량 천공구경 범위", r"천공구경 ([\d.]+)∼([\d.]+)㎜", "㎜", r"천공구경 [\d.]+∼[\d.]+㎜"),
|
||||
("그라우팅 공구손료 및 경장비 인력품 비율", r"발전기 등\)의 기계경비는 인력품의 ([\d.]+)%", "%"),
|
||||
]) # fmt: skip
|
||||
|
||||
|
||||
def b_옹벽_벌개_개간():
|
||||
T = TABS["3-08"]
|
||||
for i in (0, 1):
|
||||
ident = f"3-8-{i + 1}"
|
||||
cols, rows = one(T[i], 1, len(T[i].rows) - 1)
|
||||
add(T[i], ident, None, {}, cols, rows, notes(ident))
|
||||
cols, rows = one(T[2], 1, 2, spec=None, unit=1, qty=2)
|
||||
add(T[2], "3-8-3", None, {}, cols, rows, notes("3-8-3"))
|
||||
cols, rows = one(T[3], 1, 1)
|
||||
add(T[3], "3-8-3", "버팀목 재료량(참고)", {}, cols, rows, src="3-8-3 주④")
|
||||
coef("3-8-3", "버팀목 설치・해체 계수", "주③④", [
|
||||
("공구손료 및 경장비 인력품 비율", r"절단기 등\)의 기계경비는 인력품의 ([\d.]+)%", "%"),
|
||||
("잡재료비 주재료비 비율", r"잡재료비는 주재료\(각재\)비의 ([\d.]+)%", "%"),
|
||||
]) # fmt: skip
|
||||
cols, rows = one(T[4], 1, 4)
|
||||
add(T[4], "3-8-4", None, {}, cols, rows, notes("3-8-4"))
|
||||
# 3-9
|
||||
T = TABS["3-09"]
|
||||
t = T[0]
|
||||
cols, _ = crew(t, 2, 4)
|
||||
rows = []
|
||||
for c in (3, 4, 5):
|
||||
d = {"나무높이": rng(t.rows[1][c])}
|
||||
for r in (2, 3, 4):
|
||||
n = join(t.rows[r][0])
|
||||
d[n] = N(t.rows[r][c])
|
||||
if sp(t.rows[r]):
|
||||
d[n + "규격"] = sp(t.rows[r])
|
||||
rows.append(d)
|
||||
add(t, "3-9-1", None, {"나무높이": "범위"}, cols, rows, bigo(t, 5, 1) + notes("3-9-1"),
|
||||
rule="이상·미만 · 나무높이 m") # fmt: skip
|
||||
dist = [
|
||||
("집재거리 기준", r"집재거리는 ([\d.]+)m까지", "m"),
|
||||
("집재거리 초과 가산 단위", r"매 ([\d.]+)m\s*증가마다", "m"),
|
||||
("집재거리 초과 단위당 품 가산", r"증가마다 품을 ([\d.]+)%씩", "%"),
|
||||
]
|
||||
coef("3-9-1", "벌목 계수", "비고 · 주⑤", dist + [LOSE])
|
||||
cols, rows = one(T[1], 1, 2)
|
||||
add(T[1], "3-9-2", None, {}, cols, rows, bigo(T[1], 3, 1) + notes("3-9-2"))
|
||||
coef("3-9-2", "뿌리뽑기 계수", "비고 · 주②", dist + [
|
||||
("입목본수도 기준 범위", r"입목본수도 ([\d.]+)∼([\d.]+)%", "%", r"입목본수도 [\d.]+∼[\d.]+%"),
|
||||
("수경 기준 범위", r"수경 ([\d.]+)∼([\d.]+)㎝이하", "㎝", r"수경 [\d.]+∼[\d.]+㎝이하"),
|
||||
]) # fmt: skip
|
||||
t = T[2]
|
||||
rows = [
|
||||
{"수경(㎝)": R(re.match(r"\d+", t.rows[r][a]).group(0)),
|
||||
"연료림": N(t.rows[r][a + 1].rstrip("개")), "용재림": N(t.rows[r][a + 2].rstrip("개"))}
|
||||
for a in (0, 3)
|
||||
for r in range(1, 13)
|
||||
] # fmt: skip
|
||||
rows.sort(key=lambda d: int(d["수경(㎝)"]))
|
||||
add(
|
||||
t,
|
||||
"3-9-2",
|
||||
"입목본수도(참고 자료)",
|
||||
{"수경(㎝)": "수"},
|
||||
{"연료림": "개", "용재림": "개"},
|
||||
rows,
|
||||
src="3-9-2 참고 자료",
|
||||
)
|
||||
# 3-10
|
||||
t = TABS["3-10"][0]
|
||||
rows = [{"블록크기": rng(r[0]), "시간당작업량": N(r[1])} for r in t.rows[1:]]
|
||||
add(t, "3-10-1", None, {"블록크기": "범위"}, {"시간당작업량": "㎡/hr"}, rows, notes("3-10-1"),
|
||||
rule="이상·미만 · 블록크기 ㎡") # fmt: skip
|
||||
coef("3-10-1", "답면고르기 계수", "주②", [("물 가두기 보통인부", r"보통인부 ([\d.]+)인", "인")])
|
||||
|
||||
|
||||
def b_스마트():
|
||||
T = TABS["3-11"]
|
||||
for i, ident in {0: "3-11-1", 2: "3-11-2", 4: "3-11-3", 6: "3-11-4", 8: "3-11-5"}.items():
|
||||
cols, rows = one(T[i], 1, len(T[i].rows) - 1, spec=None, unit=1, qty=2)
|
||||
add(T[i], ident, f"{title(ident)} 장비조립・해체", {}, cols, rows, notes(ident, 0))
|
||||
for i, ident in ((1, "3-11-1"), (3, "3-11-2")):
|
||||
t, rows, prev = T[i], [], ""
|
||||
for r in range(2, len(t.rows)):
|
||||
row = t.rows[r]
|
||||
prev = row[0] or prev
|
||||
u = row[4] or (rows[-1]["단위"] if rows else "")
|
||||
d = {"공종": prev, "규격": row[1]}
|
||||
if row[3]:
|
||||
rows += [
|
||||
{**d, "작업조건": lab, "일당시공량": N(row[k]), "단위": u}
|
||||
for k, lab in ((2, "양호"), (3, "보통"))
|
||||
]
|
||||
else:
|
||||
rows.append({**d, "일당시공량": N(row[2]), "단위": u})
|
||||
cond = {"공종": "고르기", "규격": "고르기", "작업조건": "고르기"}
|
||||
add(
|
||||
t,
|
||||
ident,
|
||||
f"{title(ident)} 작업능력",
|
||||
cond,
|
||||
{"일당시공량": "줄 단위"},
|
||||
rows,
|
||||
notes(ident, 1),
|
||||
)
|
||||
for i, ident in ((5, "3-11-3"), (7, "3-11-4")):
|
||||
t = T[i]
|
||||
rows = [{"공종": t.rows[1][0], "시공량": N(t.rows[1][1]), "단위": t.rows[1][2]}]
|
||||
add(
|
||||
t,
|
||||
ident,
|
||||
f"{title(ident)} 작업능력",
|
||||
{"공종": "고르기"},
|
||||
{"시공량": "줄 단위"},
|
||||
rows,
|
||||
notes(ident, 1),
|
||||
)
|
||||
cols, rows = one(T[9], 1, 6)
|
||||
add(
|
||||
T[9],
|
||||
"3-11-5",
|
||||
"지능형 다짐공 시험시공 및 다짐관리 기준 수립",
|
||||
{},
|
||||
cols,
|
||||
rows,
|
||||
notes("3-11-5", 1),
|
||||
)
|
||||
cols, rows = across(T[10], "두께", 1, 2, 6, 4, 2)
|
||||
add(
|
||||
T[10],
|
||||
"3-11-5",
|
||||
"지능형 다짐공 본시공 작업능력",
|
||||
{"두께": "고르기"},
|
||||
cols,
|
||||
rows,
|
||||
notes("3-11-5", 2),
|
||||
)
|
||||
fuel = ("주연료 할증", r"주연료에 ([\d.]+)% 할증", "%")
|
||||
for ident, suf in (
|
||||
("3-11-1", "주⑦⑨"),
|
||||
("3-11-2", "주⑧⑩"),
|
||||
("3-11-3", "주⑦⑨"),
|
||||
("3-11-4", "주⑦⑨"),
|
||||
):
|
||||
coef(ident, f"{title(ident)} 계수", suf, [
|
||||
("적용 최소 공사규모(중규모) 토공량", r"중규모\(([\d,]+)㎥\)", "㎥"), fuel]) # fmt: skip
|
||||
coef("3-11-5", "지능형 다짐 롤러 계수", "주④⑦", [
|
||||
("적용 권장 시공 면적 이상", r"시공 면적이 ([\d,]+)㎡ 이상", "㎡"),
|
||||
("적용 권장 최소 폭 이상", r"최소 폭 ([\d.]+)m 이상", "m"),
|
||||
fuel,
|
||||
]) # fmt: skip
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
for f in (b_굴착, b_발파, b_쌓기, b_부대공, b_비탈면, b_옹벽_벌개_개간, b_스마트):
|
||||
f()
|
||||
write()
|
||||
print(len(TABLES), "표")
|
||||
@@ -3,6 +3,412 @@
|
||||
"원문": "산림품셈",
|
||||
"판": "산림청고시 제2025-82호",
|
||||
"표": [
|
||||
{
|
||||
"열쇠": "1-1-3 일일 작업시간",
|
||||
"이름": "적용방법 — 품의 일일 작업시간 기준",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-1-3 2",
|
||||
"조건": {},
|
||||
"값칸": {"일일 작업시간": "시간"},
|
||||
"줄": [
|
||||
{"일일 작업시간": 8}
|
||||
],
|
||||
"주": ["본 품셈에서 제시된 품은 일일 작업시간 8시간을 기준으로 한 것이다."]
|
||||
},
|
||||
{
|
||||
"열쇠": "1-2-1 소수 처리",
|
||||
"이름": "수량의 계산 — 소수 처리 · 유효숫자",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-2-1 1 · 2 · 6",
|
||||
"조건": {"대상": "고르기"},
|
||||
"값칸": {"반올림 소수자리": "째자리", "표시 소수자리": "째자리", "유효숫자": "자리"},
|
||||
"줄": [
|
||||
{"대상": "나무 재적 및 인원", "반올림 소수자리": 3, "반올림 소수자리원문": "소수점 셋째자리에서 반올림", "표시 소수자리": 2, "표시 소수자리원문": "둘째자리로 표시"},
|
||||
{"대상": "헥타(ha) 환산 면적", "반올림 소수자리": 3, "반올림 소수자리원문": "소수점 셋째자리에서 반올림", "표시 소수자리": 2, "표시 소수자리원문": "둘째자리로 표시"},
|
||||
{"대상": "원둘레율·삼각함수·호도", "유효숫자": 3, "유효숫자원문": "유효숫자는 3자리(3位)"}
|
||||
],
|
||||
"주": []
|
||||
},
|
||||
{
|
||||
"열쇠": "1-2-1 총축적 계산 기준",
|
||||
"이름": "총축적 계산 — 측정 기준",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-2-1 5",
|
||||
"조건": {},
|
||||
"값칸": {"측정 대상 가슴높이지름": "㎝ 이상", "가슴높이 측정 지상고": "㎝", "가슴높이지름 괄약": "㎝", "표준지 최대크기": "ha 이하"},
|
||||
"줄": [
|
||||
{"측정 대상 가슴높이지름": 6, "가슴높이 측정 지상고": 120, "가슴높이지름 괄약": 2, "표준지 최대크기": 0.04}
|
||||
],
|
||||
"주": ["가. 측정 대상입목 : 가슴높이지름 6㎝ 이상의 입목", "나. 가슴높이지름 측정부위 : 지상고 120㎝ 위치의 직경을 말하며, 2㎝ 괄약으로 측정(예 : 8㎝=7㎝ 이상 9㎝ 미만, 10㎝=9㎝ 이상 11㎝ 미만)", "(나) 표준지조사 1) 1개 표준지의 최대크기는 0.04ha 이하로 한다."]
|
||||
},
|
||||
{
|
||||
"열쇠": "1-2-1 가슴높이지름 괄약 예",
|
||||
"이름": "가슴높이지름 2㎝ 괄약 — 대표값별 범위",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-2-1 5 나",
|
||||
"조건": {"괄약 대표값(㎝)": "수"},
|
||||
"값칸": {"범위": "㎝"},
|
||||
"범위규칙": "범위(㎝) 이상·미만 — 「7㎝ 이상 9㎝ 미만」 = [7, 9]",
|
||||
"줄": [
|
||||
{"괄약 대표값(㎝)": 8, "범위": [7, 9]},
|
||||
{"괄약 대표값(㎝)": 10, "범위": [9, 11]}
|
||||
],
|
||||
"주": []
|
||||
},
|
||||
{
|
||||
"열쇠": "1-2-1 공제하지 않는 포장 구조물 자리",
|
||||
"이름": "수량 공제 제외 — 포장공종 1개소당 구조물 자리",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-2-1 9 라",
|
||||
"조건": {},
|
||||
"값칸": {"1개소당 구조물 자리": "㎡ 이하"},
|
||||
"줄": [
|
||||
{"1개소당 구조물 자리": 0.1}
|
||||
],
|
||||
"주": ["라. 포장공종의 1개소당 0.1㎡ 이하의 구조물 자리"]
|
||||
},
|
||||
{
|
||||
"열쇠": "1-2-2 설계서의 단위 및 소수의 표준",
|
||||
"이름": "설계서의 단위 및 소수의 표준",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-2-2 1",
|
||||
"조건": {"종목": "고르기"},
|
||||
"값칸": {"규격단위": "글", "규격소수자리": "자리", "수량단위": "글", "수량소수자리": "자리"},
|
||||
"줄": [
|
||||
{"종목": "공사연장", "규격단위": "m", "규격소수자리": 2, "수량단위": "m"},
|
||||
{"종목": "공사폭원", "수량단위": "m", "수량소수자리": 1},
|
||||
{"종목": "직공인부", "수량단위": "인", "수량소수자리": 2},
|
||||
{"종목": "공사 및 사업면적", "수량단위": "㎡"},
|
||||
{"종목": "용지면적", "수량단위": "㎡"},
|
||||
{"종목": "토적(높이, 너비)", "수량단위": "m", "수량소수자리": 2},
|
||||
{"종목": "토적(단면적)", "수량단위": "㎡", "수량소수자리": 1},
|
||||
{"종목": "토적(체적)", "수량단위": "㎥", "수량소수자리": 2},
|
||||
{"종목": "토적(체적합계)", "수량단위": "㎥"},
|
||||
{"종목": "떼", "규격단위": "cm", "수량단위": "㎡", "수량소수자리": 1},
|
||||
{"종목": "모래, 자갈", "규격단위": "cm", "수량단위": "㎥", "수량소수자리": 2},
|
||||
{"종목": "조약돌", "규격단위": "cm", "수량단위": "㎥", "수량소수자리": 2},
|
||||
{"종목": "견치돌, 깬돌", "짝": "가", "규격단위": "cm", "수량단위": "㎡", "수량소수자리": 1},
|
||||
{"종목": "견치돌, 깬돌", "짝": "나", "규격단위": "cm", "수량단위": "개"},
|
||||
{"종목": "야면석(野面石)", "규격단위": "cm", "수량단위": "개"},
|
||||
{"종목": "돌쌓기 및 돌붙임", "짝": "가", "규격단위": "cm", "수량단위": "㎥", "수량소수자리": 1},
|
||||
{"종목": "돌쌓기 및 돌붙임", "짝": "나", "규격단위": "cm", "수량단위": "㎡"},
|
||||
{"종목": "돌쌓기 및 돌붙임", "짝": "다", "규격단위": "cm", "수량단위": "㎥", "수량소수자리": 1},
|
||||
{"종목": "돌쌓기 및 돌붙임", "짝": "라", "규격단위": "cm", "수량단위": "㎡", "수량소수자리": 1},
|
||||
{"종목": "사석(捨石)", "규격단위": "cm", "수량단위": "㎥", "수량소수자리": 1},
|
||||
{"종목": "다듬돌(切石, 板石)", "규격단위": "cm", "수량단위": "개", "수량소수자리": 2},
|
||||
{"종목": "벽돌", "규격단위": "mm", "수량단위": "개"},
|
||||
{"종목": "블록", "규격단위": "mm", "수량단위": "개"},
|
||||
{"종목": "시멘트", "수량단위": "kg"},
|
||||
{"종목": "모르타르", "수량단위": "kg", "수량소수자리": 2},
|
||||
{"종목": "콘크리트", "수량단위": "㎥", "수량소수자리": 2},
|
||||
{"종목": "석분", "수량단위": "kg"},
|
||||
{"종목": "석회", "수량단위": "kg"},
|
||||
{"종목": "화산회", "수량단위": "kg"},
|
||||
{"종목": "아스팔트", "수량단위": "kg"},
|
||||
{"종목": "목재(판재)", "짝": "가", "규격단위": "길이m", "규격소수자리": 1, "수량단위": "㎡", "수량소수자리": 2},
|
||||
{"종목": "목재(판재)", "짝": "나", "규격단위": "폭,두께cm", "규격소수자리": 1, "수량단위": "㎥", "수량소수자리": 3},
|
||||
{"종목": "합판", "규격단위": "mm", "수량단위": "장", "수량소수자리": 1},
|
||||
{"종목": "말뚝", "짝": "가", "규격단위": "길이m", "규격소수자리": 1, "수량단위": "개"},
|
||||
{"종목": "말뚝", "짝": "나", "규격단위": "지름mm"},
|
||||
{"종목": "철강재", "규격단위": "mm", "수량단위": "kg", "수량소수자리": 3},
|
||||
{"종목": "용접봉", "규격단위": "mm", "수량단위": "kg", "수량소수자리": 1},
|
||||
{"종목": "구리판, 함석류", "수량단위": "㎡", "수량소수자리": 2},
|
||||
{"종목": "철근", "규격단위": "mm", "수량단위": "kg"},
|
||||
{"종목": "볼트・너트", "규격단위": "mm", "수량단위": "개"},
|
||||
{"종목": "꺽쇠", "규격단위": "mm", "수량단위": "개"},
|
||||
{"종목": "철선류", "규격단위": "mm", "규격소수자리": 1, "수량단위": "kg", "수량소수자리": 2},
|
||||
{"종목": "PC강선", "수량단위": "kg", "수량소수자리": 2},
|
||||
{"종목": "돌망태", "짝": "가", "규격단위": "길이m", "규격소수자리": 1, "수량단위": "m", "수량소수자리": 1},
|
||||
{"종목": "돌망태", "짝": "나", "규격단위": "지름m", "수량단위": "개"},
|
||||
{"종목": "돌망태", "짝": "다", "규격단위": "높이m"},
|
||||
{"종목": "로프류", "규격단위": "mm", "규격소수자리": 1, "수량단위": "m", "수량소수자리": 1},
|
||||
{"종목": "못", "규격단위": "길이cm", "규격소수자리": 1, "수량단위": "kg", "수량소수자리": 2},
|
||||
{"종목": "석유, 휘발유, 모빌유", "수량단위": "ℓ", "수량소수자리": 2},
|
||||
{"종목": "구리스", "수량단위": "kg", "수량소수자리": 2},
|
||||
{"종목": "넝마", "수량단위": "kg", "수량소수자리": 1},
|
||||
{"종목": "화약류", "수량단위": "kg", "수량소수자리": 3},
|
||||
{"종목": "뇌관", "수량단위": "개"},
|
||||
{"종목": "도화선", "수량단위": "m", "수량소수자리": 1},
|
||||
{"종목": "석탄, 목탄, 코크스", "수량단위": "kg", "수량소수자리": 1},
|
||||
{"종목": "산소", "수량단위": "ℓ"},
|
||||
{"종목": "카바이트", "수량단위": "kg", "수량소수자리": 1},
|
||||
{"종목": "도료(塗料)", "수량단위": "ℓ또는kg", "수량소수자리": 2},
|
||||
{"종목": "도장(塗裝)", "수량단위": "㎡", "수량소수자리": 1},
|
||||
{"종목": "관류(管類)", "짝": "가", "규격단위": "길이m", "규격소수자리": 2, "수량단위": "개"},
|
||||
{"종목": "관류(管類)", "짝": "나", "규격단위": "지름mm"},
|
||||
{"종목": "관류(管類)", "짝": "다", "규격단위": "두께mm"},
|
||||
{"종목": "수로연장", "수량단위": "m", "수량소수자리": 1},
|
||||
{"종목": "옹벽", "수량단위": "㎡", "수량소수자리": 1},
|
||||
{"종목": "승강장옹벽 및 울타리", "수량단위": "m", "수량소수자리": 1},
|
||||
{"종목": "궤도부설", "수량단위": "km", "수량소수자리": 3},
|
||||
{"종목": "시험하중", "수량단위": "ton"},
|
||||
{"종목": "보오링", "수량단위": "m", "수량소수자리": 1},
|
||||
{"종목": "방수면적", "수량단위": "㎡", "수량소수자리": 1},
|
||||
{"종목": "건물(면적)", "수량단위": "㎡", "수량소수자리": 2},
|
||||
{"종목": "건물(지붕, 벽붙이기)", "수량단위": "㎡", "수량소수자리": 1},
|
||||
{"종목": "우물", "규격단위": "깊이", "수량단위": "m", "수량소수자리": 1},
|
||||
{"종목": "마대", "수량단위": "매"}
|
||||
],
|
||||
"주": ["① 설계서 수량의 단위와 소수자리 표시는 본 표에 따르며, 반올림하여 적용한다.", "② 품셈 각 항목에서 제시한 소숫자리가 본 표의 내용과 상이할 경우 항목에서 제시하는 소숫자리를 우선하여 적용한다.", "③ 본 표에 제시하지 않은 품의 경우 유사품의 규격과 단위수량을 참고하여 적용하며, C.G.S 단위로 하는 것을 원칙으로 한다."]
|
||||
},
|
||||
{
|
||||
"열쇠": "1-2-2 금액의 단위 표준",
|
||||
"이름": "금액의 단위 표준",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-2-2 2",
|
||||
"조건": {"종목": "고르기"},
|
||||
"값칸": {"단위표기": "글", "지위": "원", "비고": "글"},
|
||||
"줄": [
|
||||
{"종목": "설계서의 총액", "단위표기": "원", "지위": 1000, "비고": "미만버림"},
|
||||
{"종목": "설계서의 소계", "단위표기": "원", "지위": 1, "비고": "미만버림"},
|
||||
{"종목": "설계서의 금액란", "단위표기": "원", "지위": 1, "비고": "미만버림"},
|
||||
{"종목": "일위대가표의 계금", "단위표기": "원", "지위": 1, "비고": "미만버림"},
|
||||
{"종목": "일위대가표의 금액란", "단위표기": "원", "지위": 0.1, "비고": "미만버림"}
|
||||
],
|
||||
"주": []
|
||||
},
|
||||
{
|
||||
"열쇠": "1-2-3 암 분류 균열간격",
|
||||
"이름": "토질 및 암의 분류 — 암 종류별 균열간격",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-2-3 2",
|
||||
"조건": {"종류": "고르기"},
|
||||
"값칸": {"균열간격": ""},
|
||||
"범위규칙": "균열간격 — 「1∼10㎝」 = [1, 10] · 「1m 이내」 = [null, 1]",
|
||||
"줄": [
|
||||
{"종류": "풍화암", "균열간격": [1, 10], "단위": "㎝"},
|
||||
{"종류": "연암", "균열간격": [10, 30], "단위": "㎝"},
|
||||
{"종류": "보통암", "균열간격": [30, 50], "단위": "㎝"},
|
||||
{"종류": "경암", "균열간격": [null, 1], "균열간격원문": "균열상태가 1m 이내", "단위": "m"}
|
||||
],
|
||||
"주": []
|
||||
},
|
||||
{
|
||||
"열쇠": "1-2-3 견치돌·깬돌·깬잡석 형상 비율",
|
||||
"이름": "돌재료 분류 — 견치돌 · 깬돌 · 깬잡석 형상 비율",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-2-3 2 차",
|
||||
"조건": {"종류": "고르기"},
|
||||
"값칸": {"접촉면의 폭": "", "접촉면의 길이": "", "전면 변의 평균길이": ""},
|
||||
"줄": [
|
||||
{"종류": "견치돌", "접촉면의 폭": "1/10", "접촉면의 폭원문": "전면 일변 길이의 1/10이상", "접촉면의 길이": "1/2", "접촉면의 길이원문": "일변 평균길이의 1/2 이상"},
|
||||
{"종류": "깬돌", "접촉면의 폭": "1/20", "접촉면의 폭원문": "전면의 일변 평균길이의 약 1/20", "접촉면의 길이": "1/3", "접촉면의 길이원문": "약 1/3"},
|
||||
{"종류": "깬잡석", "전면 변의 평균길이": "2/3", "전면 변의 평균길이원문": "뒷길이의 약 2/3"}
|
||||
],
|
||||
"주": []
|
||||
},
|
||||
{
|
||||
"열쇠": "1-2-3 돌재료 크기 분류",
|
||||
"이름": "돌재료 분류 — 크기 기준",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-2-3 2 차",
|
||||
"조건": {"종류": "고르기"},
|
||||
"값칸": {"지름": "", "뒷길이": "", "크기": ""},
|
||||
"범위규칙": "이상·이하 — 「60㎝이상」 = [60, null]",
|
||||
"줄": [
|
||||
{"종류": "잡석", "지름": [10, 30], "단위": "㎝"},
|
||||
{"종류": "큰돌", "뒷길이": [60, null], "뒷길이원문": "뒷길이 60㎝이상", "지름": [40, 100], "지름원문": "지름직경이 40∼100㎝ 이하", "단위": "㎝"},
|
||||
{"종류": "호박돌", "지름": [18, null], "단위": "㎝"},
|
||||
{"종류": "조약돌", "크기": [10, 20], "단위": "㎝"},
|
||||
{"종류": "부순돌", "지름": [0.5, 10], "단위": "㎝"},
|
||||
{"종류": "굵은 자갈", "지름": [7.5, 20], "단위": "㎝"},
|
||||
{"종류": "자갈", "지름": [0.5, 7.5], "단위": "㎝"},
|
||||
{"종류": "굵은 모래", "지름": [0.25, 2], "단위": "mm"},
|
||||
{"종류": "잔모래", "지름": [0.05, 0.25], "단위": "mm"},
|
||||
{"종류": "고로슬래그 부순돌", "크기": [0, 40], "크기원문": "0∼40mm로 파쇄 가공", "단위": "mm"},
|
||||
{"종류": "막돌", "뒷길이": [25, 40], "지름": [20, null], "지름원문": "직경 20㎝ 이상", "단위": "㎝"}
|
||||
],
|
||||
"주": []
|
||||
},
|
||||
{
|
||||
"열쇠": "1-2-3 체적의 변화 식",
|
||||
"이름": "체적의 변화 — 흐트러진상태 L · 다져진상태 C",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-2-3 3 나",
|
||||
"조건": {"기호": "고르기"},
|
||||
"값칸": {"식": "식"},
|
||||
"줄": [
|
||||
{"기호": "L", "식": "흐트러진상태의 체적(m^3)/자연상태의 체적(m^3)"},
|
||||
{"기호": "C", "식": "다져진상태의 체적(m^3)/자연상태의 체적(m^3)"}
|
||||
],
|
||||
"주": []
|
||||
},
|
||||
{
|
||||
"열쇠": "1-2-3 체적의 변화율",
|
||||
"이름": "체적의 변화율 L · C",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-2-3 3 다",
|
||||
"조건": {"종별": "고르기"},
|
||||
"값칸": {"L": "", "C": ""},
|
||||
"줄": [
|
||||
{"종별": "경암(硬岩)", "L": [1.70, 2.00], "C": [1.30, 1.50]},
|
||||
{"종별": "보통암(普通硬岩)", "L": [1.55, 1.70], "C": [1.20, 1.40]},
|
||||
{"종별": "연암(軟岩)", "L": [1.30, 1.50], "C": [1.00, 1.30]},
|
||||
{"종별": "풍화암(風化岩)", "L": [1.30, 1.35], "C": [1.00, 1.15]},
|
||||
{"종별": "폐콘크리트", "L": [1.40, 1.60], "C": "별도 설계"},
|
||||
{"종별": "호박돌(玉石)", "L": [1.10, 1.15], "C": [0.95, 1.05]},
|
||||
{"종별": "역(礫)", "L": [1.10, 1.20], "C": [1.05, 1.10]},
|
||||
{"종별": "역질토(礫質土)", "L": [1.15, 1.20], "C": [0.90, 1.00]},
|
||||
{"종별": "고결(固結)된 역질토(礫質土)", "L": [1.25, 1.45], "C": [1.10, 1.30]},
|
||||
{"종별": "모래(砂)", "L": [1.20, 1.30], "C": [0.85, 0.90]},
|
||||
{"종별": "암괴(岩塊)나 호박돌이 섞인 모래", "L": [1.40, 1.45], "C": [0.90, 0.95]},
|
||||
{"종별": "점질토", "L": [1.25, 1.35], "C": [0.85, 0.95]},
|
||||
{"종별": "역(礫)이 섞인 점질토(粘質土)", "L": [1.35, 1.40], "C": [0.90, 1.00]},
|
||||
{"종별": "암괴(岩塊)나 호박돌이 섞인 점토", "L": [1.40, 1.45], "C": [0.90, 0.95]},
|
||||
{"종별": "점토(粘土)", "L": [1.20, 1.45], "C": [0.85, 0.95]},
|
||||
{"종별": "역이섞인점질토", "L": [1.30, 1.40], "C": [0.90, 0.95]},
|
||||
{"종별": "암괴(岩塊)나호박돌이섞인점토", "L": [1.40, 1.45], "C": [0.90, 0.95]}
|
||||
],
|
||||
"주": ["암(경암ㆍ보통암ㆍ연암)을 토사와 혼합성토할 때는 공극채움으로 인한 토사량을 계상할 수 있다."]
|
||||
},
|
||||
{
|
||||
"열쇠": "1-2-3 체적환산계수 f",
|
||||
"이름": "체적환산계수(f) — 구하는 Q ÷ 기준이 되는 q",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-2-3 3 라",
|
||||
"조건": {"기준이 되는 q": "고르기"},
|
||||
"값칸": {"자연상태의 체적": "", "흐트러진 상태의 체적": "", "하져진 후의 체적": ""},
|
||||
"줄": [
|
||||
{"기준이 되는 q": "자연상태의 체적", "자연상태의 체적": 1, "흐트러진 상태의 체적": "L", "하져진 후의 체적": "C"},
|
||||
{"기준이 되는 q": "흐트러진 상태의 체적", "자연상태의 체적": "1/L", "흐트러진 상태의 체적": 1, "하져진 후의 체적": "C/L"}
|
||||
],
|
||||
"주": []
|
||||
},
|
||||
{
|
||||
"열쇠": "1-2-3 규격품 채취 시 파쇄돌 발생량",
|
||||
"이름": "토취장 및 골재원 — 규격품 채취 시 사용 못 하는 파쇄된 돌 발생량",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-2-3 4 라",
|
||||
"조건": {},
|
||||
"값칸": {"파쇄된 돌 발생량": "%"},
|
||||
"줄": [
|
||||
{"파쇄된 돌 발생량": [10, 40], "파쇄된 돌 발생량원문": "10∼40%를 표준"}
|
||||
],
|
||||
"주": []
|
||||
},
|
||||
{
|
||||
"열쇠": "1-2-3 사토장 관리 보통인부 가산",
|
||||
"이름": "사토장 관리 — 토공운반 Q값 반영 시 보통인부 노임 가산",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-2-3 5 다",
|
||||
"조건": {},
|
||||
"값칸": {"보통인부 노임 가산": ""},
|
||||
"줄": [
|
||||
{"보통인부 노임 가산": "1/8", "보통인부 노임 가산원문": "보통인부 노임의 1/8 가산"}
|
||||
],
|
||||
"주": []
|
||||
},
|
||||
{
|
||||
"열쇠": "1-2-4 사용 고재 등 발생재 공제율",
|
||||
"이름": "사용 고재 등 발생재 공제율",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-2-4 5",
|
||||
"조건": {"품명": "고르기"},
|
||||
"값칸": {"공제율": "%"},
|
||||
"줄": [
|
||||
{"품명": "사용고재(시멘트공대 및 공드람 제외)", "공제율": 90},
|
||||
{"품명": "강재스크랩(Scrap)", "공제율": 70},
|
||||
{"품명": "기타발생재", "공제율": "발생량"}
|
||||
],
|
||||
"주": ["① 공제금액 계산 : 발생량×공제율×고재단가", "② 기존시설물의 철거, 해체, 이설 등으로 인한 작업설, 부산물 등은 ‘(계약예규) 예정가격 작성기준 제17조’에 따라 재료비에서 공제하지 아니하고, 매각비용 등에 대해 별도 계상한다."]
|
||||
},
|
||||
{
|
||||
"열쇠": "1-2-6 잡재료 별도 계상",
|
||||
"이름": "공구손료 및 잡재료 — 명시 없는 잡재료 별도 계상 한도",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-2-6",
|
||||
"조건": {},
|
||||
"값칸": {"주재료비 대비": "%"},
|
||||
"줄": [
|
||||
{"주재료비 대비": [2, 5], "주재료비 대비원문": "주재료비(재료비의 할증수량 제외)의 2~5%까지"}
|
||||
],
|
||||
"주": []
|
||||
},
|
||||
{
|
||||
"열쇠": "1-2-7 소운반 포함 거리",
|
||||
"이름": "운반 — 품에 포함된 소운반 거리",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-2-7 1 가",
|
||||
"조건": {},
|
||||
"값칸": {"소운반 거리": "m 이내"},
|
||||
"줄": [
|
||||
{"소운반 거리": 20}
|
||||
],
|
||||
"주": []
|
||||
},
|
||||
{
|
||||
"열쇠": "1-2-7 경사면 소운반 거리 환산",
|
||||
"이름": "운반 — 경사면 소운반 거리(직고 대 수평거리)",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-2-7 1 나",
|
||||
"조건": {},
|
||||
"값칸": {"직고": "m", "수평거리": "m"},
|
||||
"줄": [
|
||||
{"직고": 1, "수평거리": 6}
|
||||
],
|
||||
"주": []
|
||||
},
|
||||
{
|
||||
"열쇠": "1-2-7 화물자동차 적재량",
|
||||
"이름": "화물자동차의 적재량(일반 기준)",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-2-7 3 라",
|
||||
"조건": {"종별": "고르기", "규격": "고르기"},
|
||||
"값칸": {"6톤 차량": "", "8톤 차량": "", "11톤 차량": "", "20톤 트레일러": "", "비고": "글"},
|
||||
"줄": [
|
||||
{"종별": "목재(원목)", "규격": "길이가 긴 것은 낱개", "단위": "㎥", "6톤 차량": 7.7, "8톤 차량": 10, "11톤 차량": 13},
|
||||
{"종별": "목재(제재목)", "규격": "길이가 긴 것은 낱개", "단위": "㎥", "6톤 차량": 9.0, "8톤 차량": 12, "11톤 차량": 16},
|
||||
{"종별": "경유ㆍ휘발유", "규격": "200ℓ", "단위": "드럼", "6톤 차량": 30, "8톤 차량": 40, "11톤 차량": 55},
|
||||
{"종별": "아스팔트", "규격": "200ℓ", "단위": "드럼", "6톤 차량": 24, "8톤 차량": 35, "11톤 차량": 50},
|
||||
{"종별": "새끼", "규격": "12㎜, 9.4kg", "단위": "다발", "6톤 차량": 480, "8톤 차량": 640},
|
||||
{"종별": "벽돌", "규격": "19cm×9cm×5.7cm(표준형)", "단위": "개", "6톤 차량": 2930, "8톤 차량": 3900, "11톤 차량": 5300},
|
||||
{"종별": "기와", "규격": "34cm×30cm×1.5cm", "단위": "매", "6톤 차량": 1860, "8톤 차량": 2480, "11톤 차량": 3400},
|
||||
{"종별": "보도블록", "규격": "30cm×45cm×6cm", "단위": "개", "6톤 차량": 490, "8톤 차량": 650, "11톤 차량": 890},
|
||||
{"종별": "견치돌", "규격": "뒷길이 45cm", "단위": "개", "6톤 차량": 100, "8톤 차량": 135, "11톤 차량": 180},
|
||||
{"종별": "블록", "짝": "가", "규격": "두께 10cm", "단위": "개", "6톤 차량": 650, "8톤 차량": 860, "11톤 차량": 1180},
|
||||
{"종별": "블록", "짝": "나", "규격": "두께 15cm", "단위": "개", "6톤 차량": 450, "8톤 차량": 600, "11톤 차량": 820},
|
||||
{"종별": "블록", "짝": "다", "규격": "두께 20cm", "단위": "개", "6톤 차량": 350, "8톤 차량": 460, "11톤 차량": 630},
|
||||
{"종별": "타일", "짝": "가", "규격": "두께 6㎜", "단위": "㎡", "6톤 차량": 500, "8톤 차량": 660, "비고": "모자\n이크\n포함"},
|
||||
{"종별": "타일", "짝": "나", "규격": "두께 (8㎜)", "단위": "㎡", "6톤 차량": 350, "6톤 차량원문": "(350)", "8톤 차량": 460, "8톤 차량원문": "(460)"},
|
||||
{"종별": "크링커타일", "규격": "두께 24㎜", "단위": "㎡", "6톤 차량": 150, "8톤 차량": 200},
|
||||
{"종별": "합판", "규격": "12×900×1,800㎜", "단위": "매", "6톤 차량": 450, "8톤 차량": 600, "11톤 차량": 820},
|
||||
{"종별": "유리", "규격": "두께 3㎜", "단위": "㎡", "6톤 차량": 700, "8톤 차량": 930},
|
||||
{"종별": "페인트", "짝": "가", "규격": "4ℓ(18ℓ)/통", "단위": "통", "6톤 차량": 1300, "8톤 차량": 1720, "11톤 차량": 2365},
|
||||
{"종별": "페인트", "짝": "나", "단위": "통", "6톤 차량": 300, "6톤 차량원문": "(300)", "8톤 차량": 400, "8톤 차량원문": "(400)", "11톤 차량": 550, "11톤 차량원문": "(550)"},
|
||||
{"종별": "아스타일", "규격": "3㎜×30cm×30cm", "단위": "매", "6톤 차량": 9600, "8톤 차량": 12800, "11톤 차량": 17650},
|
||||
{"종별": "흄관", "짝": "가", "규격": "ø300㎜ L=2.5m", "단위": "본", "6톤 차량": 27, "8톤 차량": 36, "11톤 차량": 52},
|
||||
{"종별": "흄관", "짝": "나", "규격": "450 "", "단위": "본", "6톤 차량": 15, "8톤 차량": 20, "11톤 차량": 27},
|
||||
{"종별": "흄관", "짝": "다", "규격": "600 "", "단위": "본", "6톤 차량": 8, "8톤 차량": 12, "11톤 차량": 15},
|
||||
{"종별": "흄관", "짝": "라", "규격": "800 "", "단위": "본", "6톤 차량": 4, "8톤 차량": 6, "11톤 차량": 9},
|
||||
{"종별": "흄관", "짝": "마", "규격": "900 "", "단위": "본", "6톤 차량": 4, "8톤 차량": 5, "11톤 차량": 7},
|
||||
{"종별": "흄관", "짝": "바", "규격": "1,000 "", "단위": "본", "6톤 차량": 3, "8톤 차량": 4, "11톤 차량": 5, "20톤 트레일러": 10},
|
||||
{"종별": "흄관", "짝": "사", "규격": "1,200 "", "단위": "본", "6톤 차량": 2, "8톤 차량": 3, "11톤 차량": 4, "20톤 트레일러": 7},
|
||||
{"종별": "흄관", "짝": "아", "규격": "1,500 "", "단위": "본", "6톤 차량": 1, "8톤 차량": 2, "11톤 차량": 2, "20톤 트레일러": 5},
|
||||
{"종별": "콘크리트관", "짝": "가", "규격": "ø250㎜ L=1m", "단위": "본", "6톤 차량": 60, "8톤 차량": 80, "11톤 차량": 110},
|
||||
{"종별": "콘크리트관", "짝": "나", "규격": "300 "", "단위": "본", "6톤 차량": 52, "8톤 차량": 70, "11톤 차량": 96},
|
||||
{"종별": "콘크리트관", "짝": "다", "규격": "350 "", "단위": "본", "6톤 차량": 42, "8톤 차량": 60, "11톤 차량": 82},
|
||||
{"종별": "콘크리트관", "짝": "라", "규격": "450 "", "단위": "본", "6톤 차량": 25, "8톤 차량": 30, "11톤 차량": 41},
|
||||
{"종별": "콘크리트관", "짝": "마", "규격": "600 "", "단위": "본", "6톤 차량": 16, "8톤 차량": 20, "11톤 차량": 27},
|
||||
{"종별": "콘크리트관", "짝": "바", "규격": "900 "", "단위": "본", "6톤 차량": 9, "8톤 차량": 12, "11톤 차량": 16},
|
||||
{"종별": "콘크리트관", "짝": "사", "규격": "1,000~1,500 "", "단위": "본", "6톤 차량": [3, 6], "8톤 차량": [4, 8], "11톤 차량": [5, 10], "20톤 트레일러": 12},
|
||||
{"종별": "주철관", "짝": "가", "규격": "ø80㎜~150㎜L=6.0m", "단위": "본", "6톤 차량": [42, 111], "8톤 차량": [46, 123]},
|
||||
{"종별": "주철관", "짝": "나", "규격": "200~450 "", "단위": "본", "6톤 차량": [9, 30], "8톤 차량": [10, 34]},
|
||||
{"종별": "주철관", "짝": "다", "규격": "200~450 "", "단위": "본", "6톤 차량": 6, "8톤 차량": [6, 9]},
|
||||
{"종별": "주철관", "짝": "라", "규격": "200~450 "", "단위": "본", "6톤 차량": 3, "8톤 차량": [3, 5]},
|
||||
{"종별": "주철관", "짝": "마", "규격": "1,000 "", "단위": "본", "6톤 차량": 2, "8톤 차량": 2},
|
||||
{"종별": "도복장강관", "짝": "가", "규격": "ø300㎜~450㎜\nL=6.0m", "단위": "본", "6톤 차량": [10, 18], "8톤 차량": [14, 22]},
|
||||
{"종별": "도복장강관", "짝": "나", "규격": "500~700 "", "단위": "본", "6톤 차량": [3, 9], "8톤 차량": [6, 10]},
|
||||
{"종별": "도복장강관", "짝": "다", "규격": "800~1,000 "", "단위": "본", "6톤 차량": [1, 3], "8톤 차량": 3},
|
||||
{"종별": "도복장강관", "짝": "라", "규격": "1,200~2,100 "", "단위": "본", "6톤 차량": 1, "8톤 차량": 1},
|
||||
{"종별": "도복장강관", "짝": "마", "규격": "2,200~2,300 "", "단위": "본", "8톤 차량": 1},
|
||||
{"종별": "P・C 파일", "짝": "가", "규격": "ø300㎜~450㎜\nL=9.0m", "단위": "본", "11톤 차량": [6, 10], "20톤 트레일러": [11, 18]},
|
||||
{"종별": "P・C 파일", "짝": "나", "규격": "450~500 "", "단위": "본", "11톤 차량": [4, 5], "20톤 트레일러": [8, 9]},
|
||||
{"종별": "시멘트", "규격": "40kg", "단위": "대", "6톤 차량": 150, "8톤 차량": 200, "11톤 차량": 275, "20톤 트레일러": 637, "20톤 트레일러원문": "637\n(25.5톤\n화물차는\n풀카고기준)"},
|
||||
{"종별": "전주", "짝": "가", "규격": "10m(일반용)", "단위": "본", "11톤 차량": 12, "20톤 트레일러": 23},
|
||||
{"종별": "전주", "짝": "나", "규격": "체신주 8m", "단위": "본", "8톤 차량": 17, "11톤 차량": 23, "20톤 트레일러": 43}
|
||||
],
|
||||
"주": []
|
||||
},
|
||||
{
|
||||
"열쇠": "1-3-1 콘크리트 및 포장용 재료",
|
||||
"이름": "재료 할증 — 콘크리트 및 포장용 재료",
|
||||
@@ -549,6 +955,65 @@
|
||||
{"구분": 2, "적용조건": "B/2 < A ≦ B 일 경우", "적용시공량": "Q = B"}
|
||||
],
|
||||
"주": ["시공량(A), 1일시공량(표준품셈)(B), 적용시공량(Q)\n※ 시공량(A)은 일반적으로 총 시공량을 적용한다. 다만, 외부환경(교통통제 및 발주물량 제한으로 “시공량/일”이 제한되는 경우 등)으로 인해 “시공량/일” 미만이 발생되는 경우 해당 시공량으로 적용한다"]
|
||||
},
|
||||
{
|
||||
"열쇠": "1-6 원가 구성 비목별 적용기준",
|
||||
"이름": "원가 구성 비목별 적용기준",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-6 라",
|
||||
"조건": {"비목 1단": "고르기", "비목 2단": "고르기", "비목 3단": "고르기"},
|
||||
"값칸": {"적용방법": "글", "요율": "%", "적용기준": "글"},
|
||||
"줄": [
|
||||
{"비목 1단": "순원가", "비목 2단": "재료비", "비목 3단": "직접재료비", "적용방법": "주재료비+잡품", "적용기준": "산림사업 표준품셈 적용기준"},
|
||||
{"비목 1단": "순원가", "비목 2단": "노무비", "비목 3단": "직접노무비", "적용기준": "산림사업 표준품셈 적용기준"},
|
||||
{"비목 1단": "순원가", "비목 2단": "노무비", "비목 3단": "간접노무비", "적용방법": "(직접노무비)×율", "적용기준": "토목·조경·산업환경설비공사\n원가계산 제비율 적용기준"},
|
||||
{"비목 1단": "순원가", "비목 2단": "경비", "비목 3단": "기계경비", "적용방법": "기계손료×장비단가", "적용기준": "산림사업 표준품셈 적용기준"},
|
||||
{"비목 1단": "순원가", "비목 2단": "경비", "비목 3단": "산업재해보상보험료", "적용방법": "(노무비 : 직노+간노)×율", "적용기준": "사업종류별 산재보험료율 적용"},
|
||||
{"비목 1단": "순원가", "비목 2단": "경비", "비목 3단": "고용보험료", "적용방법": "(노무비)×율", "적용기준": "관련 법령의 보험요율 적용"},
|
||||
{"비목 1단": "순원가", "비목 2단": "경비", "비목 3단": "국민건강보험료", "적용방법": "(직접노무비)×율", "적용기준": "관련 법령의 보험요율 적용"},
|
||||
{"비목 1단": "순원가", "비목 2단": "경비", "비목 3단": "국민연금보험료", "적용방법": "(직접노무비)×율", "적용기준": "관련 법령의 보험요율 적용"},
|
||||
{"비목 1단": "순원가", "비목 2단": "경비", "비목 3단": "노인장기요양보험료", "적용방법": "(건강보험료)×율", "적용기준": "관련 법령의 보험요율 적용"},
|
||||
{"비목 1단": "순원가", "비목 2단": "경비", "비목 3단": "산업안전보건관리비", "적용방법": "(재료비+직접노무비)×율", "적용기준": "건설업산업안전관리비계상 및\n사용기준 또는 산림청에서 적용\n하는 지정 요율 적용"},
|
||||
{"비목 1단": "순원가", "비목 2단": "경비", "비목 3단": "기타경비", "적용방법": "(재료비+노무비)×율", "적용기준": "토목·조경·산업환경설비공사\n원가계산 제비율 적용기준"},
|
||||
{"비목 1단": "순원가", "비목 2단": "경비", "비목 3단": "기타법정경비", "적용기준": "기타 법정경비 발생 시 적용"},
|
||||
{"비목 1단": "일반관리비", "적용방법": "(재료비+노무비+경비)× 율", "적용기준": "토목·조경·산업환경설비공사\n원가계산 제비율 적용기준"},
|
||||
{"비목 1단": "이윤", "적용방법": "(노무비+경비+일반관리비)× 율", "적용기준": "토목·조경·산업환경설비공사\n원가계산 제비율 적용기준"},
|
||||
{"비목 1단": "부가가치세", "적용방법": "(총원가)×율", "요율": 10, "적용기준": "부가가치세법"},
|
||||
{"비목 1단": "합계", "적용방법": "총원가+부가가치세"}
|
||||
],
|
||||
"주": ["관계 법령이나 규정에서 정하는 요율 및 노임단가는 연도별 적용기준을 확인하여 반영한다."]
|
||||
},
|
||||
{
|
||||
"열쇠": "1-7-1 거푸집 사용횟수",
|
||||
"이름": "거푸집 사용횟수",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-7-1",
|
||||
"조건": {"사용횟수(회)": "수"},
|
||||
"값칸": {"구조물": "글"},
|
||||
"줄": [
|
||||
{"사용횟수(회)": 2, "구조물": "T형보, 난간, 특히 복잡한 구조의 교각, 교대, 수문관의 본체 등 복잡한 구조"},
|
||||
{"사용횟수(회)": 3, "구조물": "슬래브, 교대, 교각, 옹벽, 파라펫트, 날개벽 등 약간 복잡한 구조"},
|
||||
{"사용횟수(회)": 4, "구조물": "측구, 수로, 확대기초, 우물통 등 비교적 간단한 구조"},
|
||||
{"사용횟수(회)": 6, "구조물": "수문 또는 관의 기초, 호안 및 보호공의 기초 등 극히 간단한 구조"}
|
||||
],
|
||||
"주": ["2. 극히 간단한 구조는 6회 이상을 적용할 수 있으며, 품은 현행품의 비율을 적용한다."]
|
||||
},
|
||||
{
|
||||
"열쇠": "1-7-4 공사용수",
|
||||
"이름": "공사용수",
|
||||
"기준": "",
|
||||
"출처": "산림품셈 1-7-4",
|
||||
"조건": {"구분": "고르기"},
|
||||
"값칸": {"수량": ""},
|
||||
"줄": [
|
||||
{"구분": "거푸집씻기", "단위": "㎥/㎡", "수량": 0.04},
|
||||
{"구분": "콘크리트혼합 및 양생", "단위": "㎥/㎥", "수량": 0.27},
|
||||
{"구분": "돌쌓기모르타르", "단위": "㎥/㎡ (표면적)", "수량": 0.06},
|
||||
{"구분": "돌씻기", "단위": "㎥/㎡ (표면적)", "수량": 0.17},
|
||||
{"구분": "모래씻기", "단위": "㎥/㎥", "수량": 0.25},
|
||||
{"구분": "잡용수", "단위": "㎥", "수량": [40, 50], "수량원문": "사용량비의 40∼50%"}
|
||||
],
|
||||
"주": ["본 표는 양생에 필요한 물의 양을 포함한 것이다."]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user