chore(resources): 옛 마스터 파일 master_data · tmp 두 폴더로 모음

- resources 아래 옛 폴더 18개 → master_data(40) · tmp(8) — 하위 폴더 없이 파일만
- 파일 이름 = 분류_대상_내용_출처_기준일 (분류: 공종 · 단가 · 품셈 · 화면)
- tmp = 실무문서 · 교본 값이 섞인 파일 — 확인 뒤 지울 것
- 코드 연결은 끊긴 채 둠(새 마스터로 다시 세움)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QqkvdyxUtANWroVKpQUyxE
This commit is contained in:
2026-09-19 13:17:40 +09:00
co-authored by Claude Opus 5
parent 834eccec2f
commit f0e44ecb13
48 changed files with 0 additions and 0 deletions
+455
View File
@@ -0,0 +1,455 @@
# -*- coding: utf-8 -*-
"""기계 원천 벌 뽑기 — 건설 품셈 제8장 md → `mach_base_2026.json` (2026-09-18 브레인).
왜 — 코덱스가 8장 md 를 손질(뭉친 표 되살리기 · 8-4 운전경비 92종 전사)했는데 자료가 앞 판 그대로였음.
품셈 벌(`_build_pum.py`)과 같은 병 — 뽑는 길이 저장소에 없어 md 가 고쳐져도 자료로 못 내려옴.
무엇을 하나
- ⚠ **줄 번호로 안 잡음.** 자리는 **기계 코드**(`5401-0015`)로 잡고, 절은 글(`8-3 기계손료` …)로 찾음.
- 8-5 기계가격 → 취득가 · 8-3 기계손료 → 손료 계수 · 8-4 운전경비 → 주연료·잡재료·조종원.
- 한 칸에 코드가 여럿인 줄(`0101-0007 0010 0012`)은 **토막 수가 맞을 때만** 읽고, 안 맞으면
`parse_audit` 에 적음(지어내지 않음). 8-4 원표는 `source_tables` 로 통째로 실어 둠.
- 이름은 `(0101) 불도저(무한궤도)` 꼴 머리글 · 가격표 `기 종` 칸 · 연료표 `기계명` 칸에서.
- **사람 판단·원문에 없는 칸은 옛 벌에서 이어받음** — 조종원 배정(121) · 암석 보정 · 붙임글 ·
손으로 맞춘 연료 줄(`parse_method`)과 손질 사유(`note`). 이어받은 것은 알림에 수로 뜸.
돌리기: `./venv/Scripts/python.exe resources/data_cost_input_value/_build_mach.py [--check]`
`--check` — 쓰지 않고 달라질 것만 알림 · 달라지면 끝 코드 1.
⚠ 금액 불변 — 기계경비는 뿌리라 움직이면 크게 움직임. 뽑은 뒤 `test_work_item_key_gate.py`
원가계산서 시험(직접공사비 5,983,724)을 돌릴 것.
"""
from __future__ import annotations
import json
import re
import sys
from datetime import datetime, timedelta, timezone
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
TARGET = ROOT / "resources/data_cost_input_value/mach_base_2026.json"
#: 절 — 글로 찾음(줄 번호 아님).
MARKS = ("8-3 기계손료", "8-4 운전경비", "8-5 기계가격")
_SEP = re.compile(r"\s*\|(\s*:?-+:?\s*\|)+\s*")
_CODE = re.compile(r"\d{4}-\d{4}")
_NAME_AT = re.compile(
r"\((\d{4})\)\s*([^()]*(?:\([^)]*\)[^()]*)*?)(?=\(\d{4}\)|\('\d\d|시\s*간\s*당|$)"
)
#: 손료표 칸 — 머리글(공백 지운 것)에 이 글이 들어 있으면 그 칸.
LOSS_COLS = {
"economic_life_hours": "내용",
"annual_standard_hours": "연간표준",
"depreciation_ratio": "상각비율",
"maintenance_ratio": "정비비율",
"annual_management_ratio": "관리비율",
}
LOSS_COEF = (
"depreciation_coefficient_1e_minus_7",
"maintenance_coefficient_1e_minus_7",
"management_coefficient_1e_minus_7",
"source_coefficient_1e_minus_7",
)
FUEL_COLS = {
"fuel_rate_l_per_hour": "주연료",
"misc_material_percent_of_fuel": "잡재료",
"operator_person_per_day": "조종원",
}
def _t(text: str) -> str:
return re.sub(r"\s+", "", str(text))
def _cells(line: str) -> list[str]:
s = line.strip()
s = s[1:-1] if s.endswith("|") else s[1:]
return [c.strip() for c in s.split("|")]
def md_tables(lines: list[str]) -> list[dict]:
tables, i = [], 0
while i < len(lines):
if (
lines[i].lstrip().startswith("|")
and i + 1 < len(lines)
and _SEP.fullmatch(lines[i + 1])
):
j = i + 2
while j < len(lines) and lines[j].lstrip().startswith("|"):
j += 1
tables.append(
{
"line": i + 1,
"headers": _cells(lines[i]),
"rows": [_cells(x) for x in lines[i + 2 : j]],
}
)
i = j
else:
i += 1
return tables
def _num(text: str) -> float | int | None:
t = _t(text).replace(",", "").rstrip("%")
t = re.sub(r"^[^\d.]*", "", t) # `휘발유0.7` · `약1.2`
if not re.fullmatch(r"\d+(\.\d+)?", t):
return None
return float(t) if "." in t else int(t)
def codes_in(cell: str, prefix: str | None) -> tuple[list[str], str | None]:
"""`0101-0007 0010` → `['0101-0007','0101-0010']`. 앞자리는 이어짐. 코드가 없으면 빈 목록."""
out = []
for token in cell.split():
if _CODE.fullmatch(token):
prefix = token[:4]
out.append(token)
elif re.fullmatch(r"\d{4}-", token): # `9020- 0010 0015` — 앞자리만 먼저 적은 줄
prefix = token[:4]
elif re.fullmatch(r"-?\d{4}", token) and prefix: # `0010` · `-0184`
out.append(f"{prefix}-{token.lstrip('-')}")
else:
return [], prefix
return out, prefix
def names_in(lines: list[str]) -> dict[str, str]:
"""`(0101) 불도저(무한궤도)` 머리글 → {앞자리: 이름}. 먼저 나온 것을 씀."""
found: dict[str, str] = {}
for line in lines:
if line.lstrip().startswith("|"):
continue
for m in _NAME_AT.finditer(line):
name = m.group(2).strip(" .·")
# ⚠ 숫자로 시작하는 이름도 있다 — `(8201) 3D GNSS 머신 가이던스(굴착기용)`.
# 첫 낱자가 숫자면 버리던 잣대가 그것을 쪽번호로 보고 내버려, 옛 벌의 뭉개진 글을
# 이어받고 있었다(2026-09-18). 낱자가 하나도 없는 토막만 버린다.
if name and re.search(r"[가-힣A-Za-z]", name):
found.setdefault(m.group(1), re.sub(r"\s+", " ", name))
return found
def split_values(cell: str, count: int) -> list[str] | None:
"""칸 하나에 값이 여럿(`9.0 12.5 14.6`) — 토막 수가 맞을 때만.
⚠ 코드가 하나인 줄은 **칸 통째로** 한 값(`320~ 400` 처럼 값 안에 공백이 있음).
"""
if count == 1:
return [cell.strip()]
parts = cell.split()
if len(parts) == count:
return parts
# 값 안에 공백이 있는 규격 칸 — `전기식 …ø45(0.75㎾) 엔진식 …ø45(2.6㎾)` 은 닫는 괄호로 가름
chunks = [x.strip() for x in re.split(r"(?<=\))\s+", cell.strip()) if x.strip()]
return chunks if len(chunks) == count else None
def parse_price(tables: list[dict], names: dict[str, str]) -> tuple[list[dict], list[dict]]:
records, bad, prefix = [], [], None
for t in tables:
if [_t(h) for h in t["headers"][:3]] != ["기종", "분류번호", "가격(₩)"]:
continue
for row in t["rows"]:
codes, prefix = codes_in(row[1], prefix)
prices = row[2].split()
if not codes or len(codes) != len(prices):
bad.append({"what": row[1][:60], "codes": len(codes), "values": len(prices)})
continue
for code, price in zip(codes, prices):
records.append({"machine_code": code, "machine_name": names.get(code[:4], ""), "price_thousand_krw": _num(price)}) # fmt: skip
return records, bad
def parse_loss(tables: list[dict], names: dict[str, str]) -> tuple[list[dict], list[dict]]:
records, bad, prefix = [], [], None
for t in tables:
head = [_t(h) for h in t["headers"]]
if not head or "분류번호" not in head[0]:
continue
cols = {field: next((i for i, h in enumerate(head) if word in h), None) for field, word in LOSS_COLS.items()} # fmt: skip
sub = next((r for r in t["rows"] if not _t(r[0])), [])
if "상각비계수" in [_t(c) for c in sub]:
first = [_t(c) for c in sub].index("상각비계수")
coef = dict(zip(LOSS_COEF, range(first, first + 4)))
elif (only := next((i for i, h in enumerate(head) if "시간당" in h), None)) is not None:
coef = {LOSS_COEF[3]: only}
else:
bad.append({"what": f"손료표 계수 칸 없음 — {t['headers']}"})
continue
# 규격 칸 — 분류번호와 첫 값 칸 사이. 둘·셋이면(5202·5203) 첫 칸만 규격으로 봄
after = min(
[i for i in list(cols.values()) + list(coef.values()) if i is not None], default=1
)
spec_col = 1 if after > 1 else None
for row in t["rows"]:
if not _t(row[0]):
continue
codes, prefix = codes_in(row[0], prefix)
values = {}
ok = bool(codes)
for field, index in {**cols, **coef}.items():
if index is None or index >= len(row):
continue
parts = split_values(row[index], len(codes)) if codes else None
if parts is None:
ok = False
break
values[field] = [_num(p) for p in parts]
specs = split_values(row[spec_col], len(codes)) if (ok and spec_col is not None and spec_col < len(row)) else None # fmt: skip
if not ok or LOSS_COEF[3] not in values:
bad.append({"what": row[0][:40], "codes": len(codes)})
continue
for n, code in enumerate(codes):
record = {"machine_code": code, "machine_name": names.get(code[:4], "")}
if specs and _t(specs[n]) not in ("", "-"):
record["specification"] = specs[n].strip()
total = values[LOSS_COEF[3]][n]
record["loss_coefficient_per_hour"] = None if total is None else total * 1e-7
for field in (*LOSS_COEF, *LOSS_COLS):
if values.get(field, [None])[n] is not None:
record[field] = values[field][n]
records.append(record)
return records, bad
def sections_by_line(lines: list[str], mark: str) -> dict[int, str]:
"""줄 번호 → 그 줄 위에서 마지막으로 본 절 제목(`8-4-3 [20]운반 및 하역기계`).
⚠ 제목이 쪽 번호와 붙어 있어(`278공통부문8-4-3 …`) **절 번호부터** 끊어 씀. 제목이 없는 동안은
앞 제목을 이어 씀 — 해상기계처럼 기종 머리글만 잇달아 나오는 자리가 있음.
"""
found, current = {}, ""
for index, text in enumerate(lines, 1):
if not text.lstrip().startswith("|"):
if m := re.search(rf"{mark}(?:-\d+)?(?=[\s\[(가-힣])", text):
current = text[m.start() :].strip()
found[index] = current
return found
def parse_fuel(
sections: dict[int, str], tables: list[dict], names: dict[str, str]
) -> tuple[list[dict], list[dict], list[dict], list[dict]]:
records, bad, source_tables, skipped, prefix = [], [], [], [], None
for t in tables:
head = [_t(h) for h in t["headers"]]
source_tables.append({"section": sections.get(t["line"], ""), "line": t["line"], "headers": t["headers"], "rows": t["rows"]}) # fmt: skip
if not head or "분류번호" not in head[0]:
continue
cols = {field: next((i for i, h in enumerate(head) if word in h), None) for field, word in FUEL_COLS.items()} # fmt: skip
spec_col = next((i for i, h in enumerate(head) if "규격" in h), None)
for row in t["rows"]:
codes, prefix = codes_in(row[0], prefix)
values, ok = {}, bool(codes)
for field, index in cols.items():
if index is None or index >= len(row):
continue
parts = split_values(row[index], len(codes))
if parts is None:
ok = False
break
values[field] = parts
if not ok or "fuel_rate_l_per_hour" not in values:
bad.append({"what": row[0][:40], "codes": len(codes)})
continue
specs = split_values(row[spec_col], len(codes)) if spec_col is not None and spec_col < len(row) else None # fmt: skip
for n, code in enumerate(codes):
raw = values["fuel_rate_l_per_hour"][n]
fuels, power = split_fuels(raw)
if not fuels:
# ⚠ 주연료가 수가 아닌 기종(전기 `㎾` · `-`)은 **연료 목록에 안 실음** — ℓ/hr 자료임.
skipped.append({"machine_code": code, "what": raw.strip()[:20]})
continue
record = {
"machine_code": code,
"machine_name": names.get(code[:4], ""),
"fuel_type": fuels[0][0],
"fuel_rate_l_per_hour": fuels[0][1],
}
# ⚠ **겸용 연료는 둘 다 싣는다** — 한쪽만 적으면 그만큼 경비가 빈다
# (`73.7+휘발유54.5` 현장가열표층재생기 · `1.87㎾ 휘발유1.3` 모르타르 믹서).
if len(fuels) > 1:
record["secondary_fuel_type"] = fuels[1][0]
record["secondary_fuel_rate_l_per_hour"] = fuels[1][1]
if power is not None:
record["electric_power_kw"] = power
for field in ("misc_material_percent_of_fuel", "operator_person_per_day"):
value = _num(values[field][n]) if field in values else None
if value is not None:
record[field] = value
if specs and _t(specs[n]) not in ("", "-"):
record["specification"] = specs[n].strip()
records.append(record)
return records, bad, source_tables, skipped
#: 겸용 연료 칸을 토막낸다 — `73.7+휘발유54.5` · `1.87㎾ 휘발유1.3` · `중유487.2`.
#: ⚠ 표시가 없으면 경유다([주]① ㉰).
_FUEL_PIECE_RE = re.compile(r"(휘발유|가솔린|중유|경유)?\s*([\d,]+(?:\.\d+)?)\s*(㎾|kW|kw)?")
def split_fuels(raw: str) -> tuple[list[tuple[str, float]], float | None]:
"""연료 칸 → (`[(연료갈래, ℓ/hr), …]`, 전력 ㎾).
한 칸에 연료가 둘인 기종이 있다 — 그 둘을 **다 싣는다**. 수가 없으면 빈 목록(전기·`-`).
"""
fuels: list[tuple[str, float]] = []
power: float | None = None
for mark, number, kw in _FUEL_PIECE_RE.findall(str(raw or "")):
value = _num(number)
if value is None:
continue
if kw: # 전력은 연료(ℓ/hr)가 아니라 곁값으로 적어 둔다
power = value
continue
kind = (
"gasoline"
if mark in ("휘발유", "가솔린")
else "heavy_oil"
if mark == "중유"
else "diesel"
)
fuels.append((kind, value))
return fuels, power
def extract(old_doc: dict, data: bytes) -> tuple[dict, dict]:
"""옛 벌 + 8장 md → (새 벌, 알림). 사람 판단 칸은 옛 벌에서 이어받음."""
lines = data.decode("utf-8").splitlines()
at = {mark: next(i for i, x in enumerate(lines) if mark in x) for mark in MARKS}
tables = md_tables(lines)
zone = lambda a, b: [t for t in tables if at[a] < t["line"] <= (at[b] if b else len(lines))] # noqa: E731
names = names_in(lines)
price, bad_price = parse_price(zone("8-5 기계가격", None), names)
loss, bad_loss = parse_loss(zone("8-3 기계손료", "8-4 운전경비"), names)
fuel, bad_fuel, source_tables, no_fuel = parse_fuel(
sections_by_line(lines, "8-4"), zone("8-4 운전경비", "8-5 기계가격"), names
)
old = old_doc["variables"]
carried = {"fuel": 0, "note": 0, "specification": 0, "machine_name": 0}
# 원문에서 못 읽은 줄은 옛 벌 값을 이어받음(손으로 맞춘 줄 · 손질 사유 · 규격)
old_fuel = {r["machine_code"]: r for r in old["mach_fuel_rate"]["parsed_records"]}
got = {r["machine_code"] for r in fuel}
# ⚠ **원문에 줄이 있는데 연료를 못 읽은 코드만** 이어받는다 —
# 못 읽은 줄(토막 수 안 맞음) · 주연료 칸이 수가 아닌 줄(`1.49㎾` 처럼 칸이 밀린 양수기).
# 원문에 아예 없는 기종은 안 이어받는다(원문에 없는 줄이 자료에 남는다).
unread = {c for row in bad_fuel for c in codes_in(row["what"], None)[0]}
unread |= {r["machine_code"] for r in no_fuel}
for code, record in old_fuel.items():
if code not in got and code in unread:
fuel.append(record)
carried["fuel"] += 1
fuel.sort(key=lambda r: r["machine_code"])
# 가격표엔 규격 칸이 없음 — 같은 코드의 손료표 규격을 씀. 연료표 규격도 손료표 것이 먼저
# (`7ton` · `0.12㎥` 처럼 단위가 붙어 있어 갈래가 갈림).
loss_spec = {r["machine_code"]: r.get("specification") for r in loss}
for record in price + fuel:
if loss_spec.get(record["machine_code"]):
record["specification"] = loss_spec[record["machine_code"]]
for records, key, field in ((loss, "mach_loss_coef", "records"), (price, "mach_price", "records"), (fuel, "mach_fuel_rate", "parsed_records")): # fmt: skip
old_by_code = {r["machine_code"]: r for r in old[key][field]}
for record in records:
was = old_by_code.get(record["machine_code"], {})
if not record.get("machine_name") and was.get("machine_name"):
record["machine_name"] = was["machine_name"] # 머리글이 없는 기종(8202~8204)
carried["machine_name"] = carried.get("machine_name", 0) + 1
if "note" in was:
record["note"] = was["note"]
carried["note"] += 1
if not record.get("specification") and was.get("specification"):
record["specification"] = was["specification"]
carried["specification"] += 1
doc = {
**old_doc,
"sources": [
{**s, "sha256": __import__("hashlib").sha256(data).hexdigest()}
for s in old_doc["sources"]
],
"variables": {
**old,
"mach_price": {**old["mach_price"], "records": price},
"mach_loss_coef": {**old["mach_loss_coef"], "records": loss},
"mach_fuel_rate": {
**old["mach_fuel_rate"],
"parsed_records": fuel,
"source_tables": source_tables,
},
},
# ⚠ 자세한 줄은 **알림에 찍음** — 자료엔 셈만 둠(이름표가 표로 잡지 않게).
"parse_audit": {
"unparsed_price_rows": len(bad_price),
"unparsed_loss_rows": len(bad_loss),
"unparsed_fuel_rows": len(bad_fuel),
"fuel_not_litre_rows": len(no_fuel),
},
}
report = {
"price": len(price), "loss": len(loss), "fuel": len(fuel),
"carried": carried,
"unparsed": {"price": len(bad_price), "loss": len(bad_loss), "fuel": len(bad_fuel)},
"unparsed_rows": {"price": bad_price, "loss": bad_loss, "fuel": bad_fuel},
"주연료가 수가 아님": len(no_fuel),
} # fmt: skip
if doc != old_doc:
doc["generated_at"] = datetime.now(timezone(timedelta(hours=9))).isoformat(
timespec="seconds"
)
return doc, report
def diff_report(old_doc: dict, doc: dict) -> dict:
"""옛 벌 ↔ 새 벌 — 갈래마다 새 코드 · 사라진 코드 · 값이 달라진 코드."""
out = {}
for key, field in (
("mach_price", "records"),
("mach_loss_coef", "records"),
("mach_fuel_rate", "parsed_records"),
):
old = {r["machine_code"]: r for r in old_doc["variables"][key][field]}
new = {r["machine_code"]: r for r in doc["variables"][key][field]}
changed = {
code: {
k: (old[code].get(k), new[code].get(k))
for k in set(old[code]) | set(new[code])
if old[code].get(k) != new[code].get(k)
}
for code in sorted(set(old) & set(new))
if old[code] != new[code]
}
out[key] = {
"새 코드": sorted(set(new) - set(old)),
"사라진 코드": sorted(set(old) - set(new)),
"값 바뀐 코드": changed,
}
return out
def main(check: bool) -> int:
old_doc = json.loads(TARGET.read_text(encoding="utf-8"))
data = (ROOT / old_doc["sources"][0]["path"]).read_bytes()
doc, report = extract(old_doc, data)
changed = doc != old_doc
diff = diff_report(old_doc, doc)
print(f"── mach_base: 가격 {report['price']} · 손료 {report['loss']} · 연료 {report['fuel']}")
print(
f" 못 읽은 줄 {report['unparsed']} · 주연료가 수가 아닌 기종 {report['주연료가 수가 아님']}"
)
print(f" 옛 벌에서 이어받음 {report['carried']}")
for kind, rows in report["unparsed_rows"].items():
for row in rows:
print(f" 못 읽음({kind}): {json.dumps(row, ensure_ascii=False)}")
for key, item in diff.items():
name_changed = sum(1 for v in item["값 바뀐 코드"].values() if set(v) == {"machine_name"})
print(
f" {key}: 새 {len(item['새 코드'])} · 사라짐 {len(item['사라진 코드'])} · "
f"값 바뀜 {len(item['값 바뀐 코드'])}(이름만 {name_changed})"
)
if changed and not check:
TARGET.write_text(json.dumps(doc, ensure_ascii=False, indent=1) + "\n", encoding="utf-8")
return 1 if (check and changed) else 0
if __name__ == "__main__":
sys.exit(main("--check" in sys.argv))
+100
View File
@@ -0,0 +1,100 @@
# -*- coding: utf-8 -*-
"""관급자재 단가 — **0 원 공시를 건너뛰고 앞 제값 공시를 채택**하는 규칙(2026-09-16 브레인).
왜 — 우리 고르기 규칙이 「물품코드마다 가장 나중 공시」라, 값이 안 실린 공시(`prce = 0`)가
옛 제값을 덮었다. 각형강관 25*25*1 이 `1,460 → 1,500 → 0` 으로 끝난 것이 그것이다.
「가격이 0」이 아니라 **그 공시에 값이 안 실린 것**이라, 0 을 값으로 받으면 안 된다.
무엇을 고치나
- 마스터의 0 원 줄만 고친다. **원천 스냅숏은 안 건드린다**(읽기만).
- 0 이 아닌 줄은 한 글자도 안 건드린다 — 규칙을 통째로 다시 돌리지 않는다.
- 고친 줄마다 **사유**(`price_adopted_reason`)를 남긴다. 조용히 바꾸지 않는다.
- 공시 날짜·번호도 **채택한 공시의 것**으로 바꾼다 — 어느 판 값인지 화면에서 보여야 한다.
- 앞 공시에도 값이 없어 0 이 남는 줄은 `zero_price_unrecovered` 로 낸다(규칙으로 못 고침).
⚠ 같은 날짜에 0 과 제값이 함께 실린다(2025-04-24) — 날짜만 보지 말고 **0 을 건너뛴 뒤**
가장 나중 공시를 고른다.
돌리기: `./venv/Scripts/python.exe resources/data_cost_input_value/_build_mat_price.py`
"""
from __future__ import annotations
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
RAW = ROOT / "resources/knowledge/original/원가계산/자재단가/나라장터_시설공통자재_2026-08-14.json"
MASTER = ROOT / "resources/data_cost_input_value/mat_price_public_2026-08-14.json"
#: 값이 안 실린 공시를 가리는 잣대 — 빈 칸·0 은 「값 없음」이다.
EMPTY_PRICES = {"", "0", "0.0", "0.00"}
SELECTION_POLICY = (
"latest notice per product identification number within the local snapshot, "
"skipping notices whose price is empty or zero "
"(a zero price means the notice carried no price, not a price of zero)"
)
UNRECOVERED_NOTE = (
"앞 공시에도 값이 없어 0 이 남은 줄. **규칙으로 못 고친다** — 0 을 값으로 쓰지 말고 "
"그 자재가 실제로 필요해지면 원천을 다시 받거나 견적을 받아야 한다."
)
def price_of(row: dict) -> str:
return str(row.get("prce", "")).strip()
def has_price(row: dict) -> bool:
return price_of(row) not in EMPTY_PRICES
def notice_day(row: dict) -> str:
return str(row.get("nticeDt", ""))[:10]
def build() -> dict:
raw = json.loads(RAW.read_text(encoding="utf-8"))
master = json.loads(MASTER.read_text(encoding="utf-8"))
records = master["variables"]["mat_price"]["records"]
history: dict[str, list[dict]] = {}
for row in raw:
history.setdefault(str(row.get("prdctIdntNo")), []).append(row)
for rows in history.values():
rows.sort(key=lambda r: str(r.get("nticeDt")), reverse=True)
repaired, unrecovered = 0, []
for record in records:
if str(record.get("price_krw")).strip() not in EMPTY_PRICES:
continue
was = notice_day(record | {"nticeDt": record.get("notice_datetime")})
adopted = next((r for r in history.get(str(record["item_code"]), ()) if has_price(r)), None)
if adopted is None:
unrecovered.append(
{
"item_code": record["item_code"],
"classification_name": record.get("classification_name", ""),
"specification": record.get("specification", ""),
"notice_datetime": record.get("notice_datetime", ""),
"reason": "이 물품코드의 모든 공시에 값이 없음",
}
)
continue
record["price_krw"] = int(price_of(adopted))
record["notice_datetime"] = str(adopted.get("nticeDt", ""))
record["notice_number"] = str(adopted.get("prceNticeNo", ""))
record["price_adopted_reason"] = f"{was} 공시에 값이 없어 {notice_day(adopted)} 값을 씀"
repaired += 1
master["selection_policy"] = SELECTION_POLICY
master["zero_price_unrecovered"] = {"note": UNRECOVERED_NOTE, "items": unrecovered}
return {"master": master, "repaired": repaired, "unrecovered": len(unrecovered)}
if __name__ == "__main__":
result = build()
MASTER.write_text(
json.dumps(result["master"], ensure_ascii=False, indent=2) + "\n", encoding="utf-8"
)
print(f"고친 줄 {result['repaired']} · 못 고친 줄 {result['unrecovered']}")
+198
View File
@@ -0,0 +1,198 @@
# -*- coding: utf-8 -*-
"""원천 벌 뽑기 — 품셈 md → `pum_forest_2026.json` · `pum_const_2026.json` (2026-09-18 브레인).
왜 — 원문 md 가 고쳐지면(줄 보탬 · 표 머리 복원) 원천 벌의 **줄 번호가 밀려** 공종 마스터가 남의 줄을
읽었음(산림 밑수 238 → 105). 머리글로 다시 맞추니 12장에 같은 머리글이 많아 **이웃 표를 집었고**
직접공사비가 조용히 움직였음. 원천 벌을 뽑던 추출기가 저장소에 없었음.
무엇을 하나
- 표 = md 의 `|` 표(머리 줄 + `|---|` 줄 + 몸 줄). 칸 글자를 **그대로** 옮김 —
⚠ 2단 머리를 펼치지 않음(윗머리 「시 간 당(10-7)」 에 밑수·절 번호가 들어 있음).
- ⚠ **줄 번호로 옛 표를 찾지 않음.** 표 번호(`F0001`·`C0001`)와 절(`section`)은 옛 벌에서 이어받되,
짝은 이렇게 지음 — ① 글자(머리·몸)가 같은 표를 **차례를 지키며** 맞댐(앞뒤가 뒤바뀌지 않으니
머리글이 같은 이웃 표로 건너뛸 수 없음) ② 맞은 표 사이 빈 자리의 표 수가 같으면 **같은 차례끼리**
(글자만 고쳐진 표) ③ 수가 다르면 그 안에서 머리가 같은 표끼리 차례대로.
- 새 표 = 가장 큰 번호 다음 번호 · 절은 표 위로 올라가며 처음 만나는 `###` 제목이나
「숫자-숫자 글」 줄(옛 벌의 절 잡는 법 — 산림 476표가 이 규칙과 같음). ⚠ 알림에 뜸 — 사람이 볼 것.
- 사라진 표 = 번호를 버리고 알림에 적음(다른 표에 번호를 물려주지 않음).
- 줄 번호·sha256 은 새 md 에서. 표가 한 칸도 안 바뀌면 파일을 그대로 둠(`generated_at` 도).
돌리기: `./venv/Scripts/python.exe resources/data_cost_input_value/_build_pum.py [--check]`
`--check` — 쓰지 않고 달라질 것만 알림 · 달라지면 끝 코드 1.
금액 불변은 `resources/tester/test_work_item_key_gate.py` 원가계산서 시험(직접공사비 5,983,724)이 잼 —
다시 뽑은 뒤 공종 마스터를 다시 짓고 그 시험을 돌릴 것.
"""
from __future__ import annotations
import difflib
import hashlib
import json
import re
import sys
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Callable
ROOT = Path(__file__).resolve().parents[2]
CATALOGS = {
"pum_forest": ROOT / "resources/data_cost_input_value/pum_forest_2026.json",
"pum_const": ROOT / "resources/data_cost_input_value/pum_const_2026.json",
}
PREFIX = {"pum_forest": "F", "pum_const": "C"}
_SEP = re.compile(r"\s*\|(\s*:?-+:?\s*\|)+\s*")
#: 옛 벌의 절 줄 — `12-2 표면 마무리를 따른다.` 처럼 본문 인용도 절로 잡혀 있음(마스터 빌더가 바로잡음).
_SECTION_LINE = re.compile(r"(\d+-\d+(?:-\d+)*\.?\s+\S.*)$")
def _cells(line: str) -> list[str]:
s = line.strip()
s = s[1:-1] if s.endswith("|") else s[1:]
return [c.strip() for c in s.split("|")]
def md_tables(lines: list[str]) -> list[dict]:
"""md `|` 표 목록 — `line` 은 머리 줄 번호(1부터)."""
tables, i = [], 0
while i < len(lines):
if (
lines[i].lstrip().startswith("|")
and i + 1 < len(lines)
and _SEP.fullmatch(lines[i + 1])
):
j = i + 2
while j < len(lines) and lines[j].lstrip().startswith("|"):
j += 1
tables.append(
{
"line": i + 1,
"headers": _cells(lines[i]),
"rows": [_cells(x) for x in lines[i + 2 : j]],
}
)
i = j
else:
i += 1
return tables
def section_above(lines: list[str], line: int) -> str | None:
"""새 표의 절 — 위로 올라가며 처음 만나는 `###` 제목 또는 「숫자-숫자 글」 줄(표 줄은 건너뜀)."""
for k in range(line - 2, -1, -1):
s = lines[k]
if s.startswith("### "):
return s[4:].strip()
if s.lstrip().startswith("|"):
continue
if m := _SECTION_LINE.search(s):
return m.group(1).strip()
return None
def _sig(table: dict) -> str:
return json.dumps([table["headers"], table["rows"]], ensure_ascii=False)
def align(old: list[dict], new: list[dict]) -> list[int | None]:
"""`new[j]` 의 짝 `old` 차례(없으면 None). 차례를 지킴 — 짝끼리 앞뒤가 뒤바뀌지 않음."""
pair: list[int | None] = [None] * len(new)
def pair_blocks(a: list[str], b: list[str], i0: int, j0: int, inner: Callable | None) -> None:
matcher = difflib.SequenceMatcher(None, a, b, autojunk=False)
for tag, i1, i2, j1, j2 in matcher.get_opcodes():
if tag == "equal" or (tag == "replace" and i2 - i1 == j2 - j1):
for k in range(j2 - j1):
pair[j0 + j1 + k] = i0 + i1 + k
elif tag == "replace" and inner:
inner(i0 + i1, i0 + i2, j0 + j1, j0 + j2)
def by_headers(i1: int, i2: int, j1: int, j2: int) -> None:
heads = lambda ts: [json.dumps(t["headers"], ensure_ascii=False) for t in ts] # noqa: E731
matcher = difflib.SequenceMatcher(
None, heads(old[i1:i2]), heads(new[j1:j2]), autojunk=False
)
for tag, a1, a2, b1, b2 in matcher.get_opcodes():
if tag == "equal":
for k in range(b2 - b1):
pair[j1 + b1 + k] = i1 + a1 + k
pair_blocks([_sig(t) for t in old], [_sig(t) for t in new], 0, 0, by_headers)
return pair
def extract(old_doc: dict, read: Callable[[str], bytes], dataset: str) -> tuple[dict, dict]:
"""옛 벌 + 원문 → (새 벌, 알림). `read(path)` 는 원문 파일 바이트."""
old_tables = old_doc["variables"]["pum"]["tables"]
per_file = "source_file" in old_tables[0]
sources = [s for s in old_doc["sources"] if s.get("role") == "primary"]
numbers = [int(t["table_id"][1:]) for t in old_tables]
next_number = max(numbers) + 1
report = {"same": 0, "moved": 0, "changed": [], "new": [], "removed": []}
tables, new_sources = [], []
for src in old_doc["sources"]:
data = read(src["path"])
new_sources.append({**src, "sha256": hashlib.sha256(data).hexdigest()})
for src in sources:
lines = read(src["path"]).decode("utf-8").splitlines()
old = [t for t in old_tables if not per_file or t["source_file"] == src["path"]]
new = md_tables(lines)
for j, k in enumerate(align(old, new)):
t = new[j]
if k is None:
table_id = f"{PREFIX[dataset]}{next_number:04d}"
next_number += 1
section = section_above(lines, t["line"])
report["new"].append({"table_id": table_id, "line": t["line"], "section": section})
else:
table_id, section = old[k]["table_id"], old[k]["section"]
if _sig(old[k]) != _sig(t):
report["changed"].append({"table_id": table_id, "line": t["line"]})
elif old[k]["line"] != t["line"]:
report["moved"] += 1
else:
report["same"] += 1
entry = {"table_id": table_id, "section": section, "line": t["line"], "headers": t["headers"], "rows": t["rows"]} # fmt: skip
if per_file:
entry["source_file"] = src["path"]
tables.append(entry)
kept = {old[k]["table_id"] for k in align(old, new) if k is not None}
report["removed"] += [{"table_id": t["table_id"], "section": t["section"]} for t in old if t["table_id"] not in kept] # fmt: skip
doc = {**old_doc, "sources": new_sources}
doc["variables"] = {
**old_doc["variables"],
"pum": {**old_doc["variables"]["pum"], "tables": tables},
}
if doc != old_doc:
doc["generated_at"] = datetime.now(timezone(timedelta(hours=9))).isoformat(
timespec="seconds"
)
return doc, report
def _read(path: str) -> bytes:
return (ROOT / path).read_bytes()
def main(check: bool) -> int:
dirty = False
for dataset, path in CATALOGS.items():
old_doc = json.loads(path.read_text(encoding="utf-8"))
doc, report = extract(old_doc, _read, dataset)
changed = doc != old_doc
dirty |= changed
print(
f"── {dataset}: 표 {len(doc['variables']['pum']['tables'])} · 그대로 {report['same']} · "
f"줄만 밀림 {report['moved']} · 글자 바뀜 {len(report['changed'])} · 새 표 {len(report['new'])} · "
f"사라진 표 {len(report['removed'])}" + ("" if changed else " — 바뀐 것 없음")
)
for key in ("changed", "new", "removed"):
for item in report[key]:
print(f" {key}: {json.dumps(item, ensure_ascii=False)}")
if changed and not check:
path.write_text(json.dumps(doc, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
return 1 if (check and dirty) else 0
if __name__ == "__main__":
sys.exit(main("--check" in sys.argv))
@@ -0,0 +1,29 @@
{
"schema_version": "1.0",
"dataset_id": "data_work_item_master_manifest",
"generated_at": "2026-09-18T21:50:29+09:00",
"built_by": "B08_Quantity/B08_Quantity_Build_WorkItemMaster.py",
"source": {
"dataset_id": "pum_const",
"effective_date": "2026-01-01",
"sha256": "861a6a8c5b7d74fa21d25f4f4422d1ab34d85f98ff1d5a3e67ed9d4945250611",
"file": "pum_const_2026.json"
},
"files": [
{
"file": "const_work_item_master_2026-01-01.json",
"sha256": "25c082c2ca696a547bf6363c3cca92c039505759490936122d7d5b8c8b028ee1",
"size_bytes": 4740067
},
{
"file": "const_form_undetermined_2026-01-01.json",
"sha256": "8e61664196a17d13745e4f753596696e559022aa146f5770044ebcc8a811dc76",
"size_bytes": 90252
},
{
"file": "const_basis_missing_2026-01-01.json",
"sha256": "aea06bc8697639f8a131a106b81b4a8a7326ba11195f5c88a33f715556da8f6a",
"size_bytes": 118388
}
]
}
@@ -0,0 +1,29 @@
{
"schema_version": "1.0",
"dataset_id": "data_work_item_master_manifest",
"generated_at": "2026-09-18T20:30:25+09:00",
"built_by": "B08_Quantity/B08_Quantity_Build_WorkItemMaster.py",
"source": {
"dataset_id": "pum_forest",
"effective_date": "2026-01-01",
"sha256": "d44394af44039bfa1654e67c2f161e8b45c9b87a1cab90fb5a5239e3c2f579bd",
"file": "pum_forest_2026.json"
},
"files": [
{
"file": "work_item_master_2026-01-01.json",
"sha256": "435e9427c9b7fba46fc0c5ad41733198d623d30ad2fd752a6e022738550669ea",
"size_bytes": 1095122
},
{
"file": "form_undetermined_2026-01-01.json",
"sha256": "973ea53279095a574157daf5fa13d3b35379af1c54df5debeee7f3ed934d943e",
"size_bytes": 245
},
{
"file": "basis_missing_2026-01-01.json",
"sha256": "3ef647276c8b93c5f1532ef8d6c0e83f6d8154dbd392c06fc71d148b0ad65d78",
"size_bytes": 18788
}
]
}
@@ -0,0 +1,137 @@
{
"schema_version": "1.0",
"dataset_id": "data_cost_input_value_manifest",
"generated_at": "2026-08-15T08:10:50+09:00",
"policy": {
"no_direct_download_performed": true,
"no_invented_values": true,
"no_null_placeholders": true,
"specification_catalogs_are_transferred_as_complete_local_source_tables": true
},
"files": [
{
"file": "coef_2026.json",
"dataset_id": "coef",
"effective_date": "2026-01-01",
"sha256": "daba170cd806374c92e086dec69f9559271c0317a7426e66e9dc841c2ca264bd",
"size_bytes": 31715
},
{
"file": "fx_2026-08-14.json",
"dataset_id": "fx",
"effective_date": "2026-08-14",
"sha256": "9b3325d2cc7f4508d57b677233cd9a2ffdf6d15ff9eabfc454190ee497f0337e",
"size_bytes": 1142
},
{
"file": "labor_const_2026-01-01.json",
"dataset_id": "labor_const",
"effective_date": "2026-01-01",
"sha256": "4a7bf8ed334103a7bf166e4499314061813caacff1a12adea7d595a508ca7e14",
"size_bytes": 32828
},
{
"file": "labor_mfg_2026-07-01.json",
"dataset_id": "labor_mfg",
"effective_date": "2026-07-01",
"sha256": "cef66bec83c1a07592ff26dbd557f9f0c46e1f7dfd2919447f3bec103562c73b",
"size_bytes": 38189
},
{
"file": "mach_base_2026.json",
"dataset_id": "mach_base",
"effective_date": "2026-01-01",
"sha256": "b66e9f658da2ed4a02931b3a220cb81f7bce0ba845b92d7562ae2c27b62dda1f",
"size_bytes": 457054
},
{
"file": "mat_price_public_2026-08-14.json",
"dataset_id": "mat_price_public",
"effective_date": "2026-08-14",
"sha256": "75ddfe85b0140d782cd00187ec459e995cd0db9982e7bd48f62f83cd65248acc",
"size_bytes": 4748976
},
{
"file": "oil_2026-08-14.json",
"dataset_id": "oil",
"effective_date": "2026-08-14",
"sha256": "c2d9f95ff5d6c8a6624298c2f2d43b581a7e5bbc3be64a5af15c763b804e0a7a",
"size_bytes": 879
},
{
"file": "oil_regional_2026-09-09.json",
"dataset_id": "oil_regional",
"effective_date": "2026-09-09",
"sha256": "f9c1ce162b326b324604a2681a6078cccbc11624bbdbab6f0791e4368140b659",
"size_bytes": 5249
},
{
"file": "pum_const_2026.json",
"dataset_id": "pum_const",
"effective_date": "2026-01-01",
"sha256": "dbc46ef1316b83ed710f25bc402dcecc4a979bbfc4a6641a0554252bca326ca7",
"size_bytes": 3095263
},
{
"file": "pum_forest_2026.json",
"dataset_id": "pum_forest",
"effective_date": "2026-01-01",
"sha256": "111208fd482dcfc3b8c8dd58004b153382fc46555d0d00985c19b45ebbc2e0dd",
"size_bytes": 916135
},
{
"file": "rates_2026.json",
"dataset_id": "rates",
"effective_date": "2026-04-13",
"sha256": "ac0328df68c70b7c0643bf1b9b1ad69e6125bf2ae3cbfda9825519600fde5a87",
"size_bytes": 39623
}
],
"deferred": [
{
"variable": "mat_rebar",
"reason": "official shopping mall snapshot not locally stored"
},
{
"variable": "mat_remicon",
"reason": "official shopping mall snapshot not locally stored"
},
{
"source_group": "asphalt_concrete",
"reason": "official shopping mall snapshot not locally stored"
},
{
"variable": "pum_std_drawing",
"reason": "구조물 표준도 원단위(옹벽·날개벽·집수정 규격별 콘크리트·거푸집·철근량)를 담을 자리. 품셈·교본에 표가 없어 실무 구조물 일람표에서 뽑아야 하는데 그 자료가 아직 안 들어옴 — 자료 확보 대기",
"source_needed": "실무 구조물 일람표(옹벽·날개벽·집수정 규격별 수량표)",
"knowledge_ref": "resources/knowledge/technical_info/01_임도/05_원가정보/원가_입력변수_사전.md G6 — 「현행 공백 E1(실무 일람표 추출 필요)」"
}
],
"program_entry": [
{
"source_group": "paid_price_guides",
"responsibility": "user_or_program_input"
},
{
"source_group": "vendor_quotes",
"responsibility": "user_or_program_input"
}
],
"selection_pending": [
{
"variable": "coef_soil_L/coef_soil_C — 원문 중복 2행 채택",
"detail": "품셈 원문이 '역이 섞인 점질토'(값 상이 2행)·'암괴나 호박돌이 섞인 점토'(동일값 2행)를 중복 수록 — 원문 그대로 수록(duplicate_group·selection:pending). 프로그램 기본 채택행은 개발 단계 사용자 선정",
"candidates": [
"duplicate_index_1",
"duplicate_index_2"
]
},
{
"variable": "rate_environment",
"candidates": [
"road_0.9_percent",
"other_civil_0.8_percent"
]
}
]
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,561 @@
{
"schema_version": "1.0",
"dataset_id": "work_item_master_basis_missing",
"effective_date": "2026-01-01",
"note": "밑수(「10㎡당」 같은 기준 수량)를 못 찾은 표. **1 단위당으로 단정하지 말 것** — 곱셈이 10배·100배 틀린다. 값을 곱해야 하는 형태(requirement·productivity)만 담는다.",
"items": [
{
"pum_table_id": "F0042",
"section": "2-1-1. 휘발유․오일",
"pum_form": "requirement",
"line": 1308,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0043",
"section": "2-1-1. 휘발유․오일",
"pum_form": "requirement",
"line": 1331,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0046",
"section": "2-1-1. 휘발유․오일",
"pum_form": "requirement",
"line": 1356,
"why": "원문 부재 — 사람 판정: 원문에 밑수 없음 · 비고의 「1인당」은 밑수가 아님 — 기계 한 대에 붙는 인원 말이다(「인은 밑수가 아니다」 2026-09-09 판정이 옳았음 · PDF 대조로 확인)"
},
{
"pum_table_id": "F0057",
"section": "2-1-7. 소각, 매몰, 훈증, 박피",
"pum_form": "requirement",
"line": 1521,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0061",
"section": "2-1-11. 지상 약제살포",
"pum_form": "requirement",
"line": 1573,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0062",
"section": "2-1-12. 그물망 피복",
"pum_form": "requirement",
"line": 1582,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0078",
"section": "3-4-3. 임산물 운반로 및 작업로 보수비 산정",
"pum_form": "requirement",
"line": 1818,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0083",
"section": "4-1-1. 임업용 동력기계톱",
"pum_form": "productivity",
"line": 1932,
"why": "원문 부재 — 거절 자리: 원문이 적은 것이 '(단위 : ㎥/1인/1일)' 라 밑수로 안 받음"
},
{
"pum_table_id": "F0084",
"section": "4-1-2. 하베스터(부착형 스트로크)",
"pum_form": "productivity",
"line": 1950,
"why": "원문 부재 — 거절 자리: 원문이 적은 것이 '(단위 : ㎥/1인/1일)' 라 밑수로 안 받음"
},
{
"pum_table_id": "F0085",
"section": "4-1-2. 하베스터(부착형 스트로크)",
"pum_form": "requirement",
"line": 1960,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0091",
"section": "4-6. 벌목부 작업안전 보조",
"pum_form": "requirement",
"line": 2066,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0095",
"section": "5-1-3. 교목굴취(근원직경)",
"pum_form": "requirement",
"line": 2160,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0097",
"section": "5-1-5. 떼운반 적재 기준표",
"pum_form": "requirement",
"line": 2181,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0108",
"section": "5-3-5. 교목식재(흉고직경)",
"pum_form": "requirement",
"line": 2359,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0127",
"section": "5-19-2. 표토펴기 및 고르기",
"pum_form": "requirement",
"line": 2672,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0154",
"section": "6-2-1. 둘레베기",
"pum_form": "requirement",
"line": 3099,
"why": "원문 부재 — 거절 자리: 원문이 적은 것이 '(1ha당, 100본당)' 라 밑수로 안 받음"
},
{
"pum_table_id": "F0155",
"section": "6-2-2. 줄베기",
"pum_form": "requirement",
"line": 3109,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0156",
"section": "6-2-3. 모두베기",
"pum_form": "requirement",
"line": 3125,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0157",
"section": "6-3. 맹아제거",
"pum_form": "requirement",
"line": 3142,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0158",
"section": "6-4-1. 덩굴걷기",
"pum_form": "requirement",
"line": 3163,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0159",
"section": "6-4-2. 덩굴 약제 살포처리",
"pum_form": "requirement",
"line": 3173,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0160",
"section": "6-4-3. 소금처리",
"pum_form": "requirement",
"line": 3184,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0161",
"section": "6-4-4. 뿌리제거",
"pum_form": "requirement",
"line": 3203,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0163",
"section": "6-5. 어린나무 가꾸기",
"pum_form": "requirement",
"line": 3264,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0169",
"section": "7-1-1. 수확",
"pum_form": "productivity",
"line": 3381,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0170",
"section": "7-1-1. 수확",
"pum_form": "requirement",
"line": 3391,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0171",
"section": "7-1-1. 수확",
"pum_form": "requirement",
"line": 3401,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0172",
"section": "7-1-2. 숲가꾸기, 병해충방제",
"pum_form": "requirement",
"line": 3415,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0174",
"section": "7-3. 아키야윈치(임업용 윈치) 집재",
"pum_form": "requirement",
"line": 3459,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0175",
"section": "7-4-1. 수확",
"pum_form": "requirement",
"line": 3472,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0176",
"section": "7-4-2. 숲가꾸기, 산림병해충방제",
"pum_form": "requirement",
"line": 3496,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0177",
"section": "7-5-1. 수확",
"pum_form": "requirement",
"line": 3516,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0178",
"section": "7-5-2. 숲가꾸기, 병해충방제",
"pum_form": "requirement",
"line": 3539,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0179",
"section": "7-6. 스윙야더 집재",
"pum_form": "requirement",
"line": 3562,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0180",
"section": "7-7-1. 수확",
"pum_form": "requirement",
"line": 3576,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0182",
"section": "7-8-1. 가선설치",
"pum_form": "requirement",
"line": 3627,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0183",
"section": "7-8-2. 가선해체",
"pum_form": "requirement",
"line": 3639,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0184",
"section": "7-8-3. 집재 소요인력",
"pum_form": "requirement",
"line": 3651,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0192",
"section": "7-12. 동력상하차기(우드그래플) 집적",
"pum_form": "requirement",
"line": 3783,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0195",
"section": "7-14. 원목 운반-수확",
"pum_form": "productivity",
"line": 3869,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0196",
"section": "7-15-1. 수확",
"pum_form": "productivity",
"line": 3889,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0197",
"section": "7-15-2 숲가꾸기, 병해충방제",
"pum_form": "productivity",
"line": 3903,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0198",
"section": "7-16. 소형 포워더 운재",
"pum_form": "productivity",
"line": 3923,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0199",
"section": "7-17. 소형트럭 운재",
"pum_form": "productivity",
"line": 3937,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0200",
"section": "7-18. 지조운반(포워더 활용)",
"pum_form": "productivity",
"line": 3951,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0202",
"section": "8-1-2. 약제주입병",
"pum_form": "requirement",
"line": 3989,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0203",
"section": "8-2-1. 소나무재선충병",
"pum_form": "requirement",
"line": 4007,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0204",
"section": "8-2-1. 소나무재선충병",
"pum_form": "requirement",
"line": 4024,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0206",
"section": "8-2-1. 소나무재선충병",
"pum_form": "requirement",
"line": 4048,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0207",
"section": "8-2-1. 소나무재선충병",
"pum_form": "requirement",
"line": 4066,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0209",
"section": "8-2-2. 솔잎혹파리",
"pum_form": "requirement",
"line": 4105,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0210",
"section": "8-2-2. 솔잎혹파리",
"pum_form": "requirement",
"line": 4133,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0211",
"section": "8-2-2. 솔잎혹파리",
"pum_form": "requirement",
"line": 4161,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0213",
"section": "8-2-3. 솔껍질깍지벌레",
"pum_form": "requirement",
"line": 4207,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0214",
"section": "8-2-3. 솔껍질깍지벌레",
"pum_form": "requirement",
"line": 4235,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0215",
"section": "8-2-3. 솔껍질깍지벌레",
"pum_form": "requirement",
"line": 4263,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0217",
"section": "8-2-4. 솔나방",
"pum_form": "requirement",
"line": 4304,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0219",
"section": "8-2-5. 푸사리움가지마름병",
"pum_form": "requirement",
"line": 4345,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0220",
"section": "8-3. 소각, 매몰, 훈증, 박피",
"pum_form": "productivity",
"line": 4373,
"why": "원문 부재 — 거절 자리: 원문이 적은 것이 '(㎥/인)' 라 밑수로 안 받음"
},
{
"pum_table_id": "F0230",
"section": "8-6-3. 지상방제",
"pum_form": "requirement",
"line": 4582,
"why": "원문 부재 — 거절 자리: 원문이 적은 것이 '(단위 : 인)' 라 밑수로 안 받음"
},
{
"pum_table_id": "F0232",
"section": "8-7. 방제 실행 등록",
"pum_form": "requirement",
"line": 4608,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0236",
"section": "8-10. 그물망 피복",
"pum_form": "requirement",
"line": 4656,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0237",
"section": "8-11. 이동식 임목 파쇄",
"pum_form": "productivity",
"line": 4674,
"why": "미판독 — 원문이 '(단위 : 1대 1조, 시간당)' 라고 적었는데 우리가 못 읽음"
},
{
"pum_table_id": "F0238",
"section": "9-2. 노선 굴진 보조원",
"pum_form": "requirement",
"line": 4700,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0251",
"section": "9-8-1. T=30㎝ 미만",
"pum_form": "requirement",
"line": 4885,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0304",
"section": "10-6-3. 기타 임업자재",
"pum_form": "requirement",
"line": 5696,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0317",
"section": "10-10-1. 콘크리트 및 골재운반(지상)",
"pum_form": "requirement",
"line": 5920,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0318",
"section": "10-10-2. 그 외 자재의 운반품셈",
"pum_form": "requirement",
"line": 5930,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0325",
"section": "11-1. 콘테이너형 가설건축물",
"pum_form": "requirement",
"line": 6062,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0337",
"section": "12-5. 문양거푸집(0~7m)",
"pum_form": "requirement",
"line": 6261,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0350",
"section": "12-12. 날개벽",
"pum_form": "requirement",
"line": 6465,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0355",
"section": "12-17-1. 펌프카 타설",
"pum_form": "requirement",
"line": 6553,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0362",
"section": "12-2 표면 마무리를 따른다.",
"pum_form": "requirement",
"line": 6623,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0379",
"section": "12-29. 스페이셔 설치(몰탈 블록)",
"pum_form": "requirement",
"line": 6829,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0401",
"section": "13-2-4. 야면석 채집(인력)",
"pum_form": "requirement",
"line": 7079,
"why": "원문 부재 — 거절 자리: 원문이 적은 것이 '(단위: 인 당)' 라 밑수로 안 받음"
},
{
"pum_table_id": "F0405",
"section": "13-3. 기초다짐 및 뒤채움’ 항을 적용한다.",
"pum_form": "requirement",
"line": 7141,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0437",
"section": "13-12-1. 뭉기기",
"pum_form": "requirement",
"line": 7735,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0438",
"section": "13-12-2. 지오셀(사면보강)",
"pum_form": "requirement",
"line": 7748,
"why": "원문 부재 — 원문에 밑수가 아예 없음(채우면 지어내는 꼴)"
},
{
"pum_table_id": "F0447",
"section": "13-15-2. 목책 설치",
"pum_form": "requirement",
"line": 7868,
"why": "원문 부재 — 거절 자리: 원문이 적은 것이 '(단위: 인/조)' 라 밑수로 안 받음"
}
]
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,7 @@
{
"schema_version": "1.0",
"dataset_id": "work_item_master_form_undetermined",
"effective_date": "2026-01-01",
"note": "형태를 못 정한 표. 사람이 보고 productivity/requirement/coefficient 로 확정할 것.",
"items": []
}
@@ -0,0 +1,640 @@
{
"schema_version": "1.0",
"dataset_id": "work_item_link",
"effective_date": "2026-01-01",
"note": "산림사업 표준품셈과 건설공사 표준품셈의 연결 밑그림. 확정된 단일 절은 FW-/CW- 불변 열쇠로 잇고, 원문이 건설 절을 밝히지 않거나 구판-현행 대응이 확인되지 않은 행은 열쇠 없이 미판정으로 둔다.",
"policy": {
"both_precedence": "forest",
"fallback": "construction",
"applies_to": "branch=both,status=확정",
"unconfirmed_action": "do_not_link",
"source": "산림사업 표준품셈 총칙 1-1 제7호, 원문 L535",
"rule": "산림품셈에 명시된 품은 산림값을 우선하고, 명시되지 않은 품만 건설 등 타 부문 표준품셈으로 보완한다."
},
"links": [
{
"forest_key": "",
"const_key": "",
"branch": "forest_only",
"evidence": "산림 제2장 조림·제3장 숲가꾸기·제4장 산림병해충방제·제6장 산림경영계획·제7장 수확; 2026 건설품셈 5부문 45장에서 같은 산림사업 공종표 미확인",
"difference_note": "㉮ 근거가 될 절이 아예 없음 — 조경·유지관리의 비슷한 이름을 같은 공종으로 볼 근거가 없음",
"status": "미판정"
},
{
"forest_key": "",
"const_key": "",
"branch": "forest_only",
"evidence": "산림 13-13-1 목재틀흙막이; 2026 건설품셈에서 같은 구조·규격별 표 미확인",
"difference_note": "㉮ 근거가 될 절이 아예 없음 — 건설품셈 전체에서 '목재틀흙막이'·'목재틀' 절을 찾지 못함",
"status": "미판정"
},
{
"forest_key": "",
"const_key": "CW-00844",
"branch": "const_only",
"evidence": "산림 13-4 돌쌓기에는 배합 원단위 없음; 건설 건축 9-1-1 모르타르 배합 원문 L6~22; masonry_wet AX-WK-c0842a0d 사용",
"difference_note": "1㎥당 1:3 배합은 시멘트 510kg·모래 1.10㎥·보통인부 0.66인",
"status": "확정"
},
{
"forest_key": "",
"const_key": "CW-00018",
"branch": "const_only",
"evidence": "산림 1-3-1 재료의 할증에 없는 재료를 건설 공통 1-3-1로 보완; material_surcharge_2026-01-01.json source.supplementary",
"difference_note": "산림표가 정본이며 산림표에 없는 재료만 건설표 사용",
"status": "확정"
},
{
"forest_key": "FW-00260",
"const_key": "CW-00330",
"branch": "both",
"evidence": "산림 9-7-1 원문 L4814~4828이 건설 공통 8-2-13을 직접 참조하고 토목·산림의 적용값을 비교",
"difference_note": "버킷계수 K와 체적환산계수 f는 같지만 작업효율 E는 임도 0.35·토목 0.45(임도가 0.10 낮음), 사이클 타임은 임도 180°·22초와 토목 90°·18초(임도가 4초, 22.2% 김). 임도의 협소한 공간과 추락위험을 반영한 차이",
"status": "확정"
},
{
"forest_key": "",
"const_key": "",
"branch": "both",
"evidence": "산림 9-21 원문 L5510~5527; 건설 공통 3-9-2 원문 L692~703; 현행 계산은 건설표의 1,000㎡당 밑수만 사용",
"difference_note": "㉰ 축이 달라 못 이음 — 굴삭기 0.2㎥ 기준 산림은 소밀도별 0.80/1.01/1.22hr, 건설은 굴삭기+그래플 3.76hr/1,000㎡로 소밀도끼리도 2.96hr·4.7배 차이. 품은 산림값을 쓰고 건설에서는 단위 밑수만 빌림",
"status": "미판정"
},
{
"forest_key": "",
"const_key": "",
"branch": "both",
"evidence": "산림 13-4-4 원문 L7132 이후; 건설 공통 7-1 돌쌓기; stone_kind_2026-01-01.json 구판 건설표 fallback",
"difference_note": "㉰ 축이 달라 못 이음 — 산림은 석재 종류·찰/메·뒷길이별 ㎡당 품. 건설은 3인+1인+굴삭기 작업조의 1일 시공량으로 뒷길이 35/55/75cm에서 메 23/26/28㎡·찰 27/30/34㎡. 표 축·포함범위가 달라 직접 치환 불가",
"status": "미판정"
},
{
"forest_key": "",
"const_key": "",
"branch": "both",
"evidence": "산림 10-4·10-9 운반식·적재량; 건설 공통 8-1-6(2) 운반거리·속도·왕복시간; B09 운반 계산에서 부분 결합",
"difference_note": "산림은 현장 공종·적재 조건, 건설은 운반 시간 산정층을 제공하므로 어느 한쪽으로 대체하지 않음",
"status": "확정"
},
{
"forest_key": "",
"const_key": "",
"branch": "both",
"evidence": "산림 1-3-2 및 임도 토공 표; 건설 공통 1-3-2 및 토공 관련 표",
"difference_note": "㉯ 이름은 닮았으나 내용이 다름 — 토량환산 표의 산림 토질 행에 같은 이름처럼 보이면서 서로 다른 계수가 있어 이름만으로 수치 대응 불가",
"status": "미판정"
},
{
"forest_key": "FW-00016",
"const_key": "CW-00018",
"branch": "both",
"evidence": "산림 1-3-1과 건설 공통 1-3-1 재료의 할증; material_surcharge_2026-01-01.json",
"difference_note": "같은 재료는 산림값 우선, 산림에 없는 재료만 건설값 사용",
"status": "확정"
},
{
"forest_key": "FW-00008",
"const_key": "CW-00009",
"branch": "both",
"evidence": "산림 1-2-2 단위 표준 원문 L573~664; 건설 공통 1-2-2 단위표준 원문 L46~99",
"difference_note": "㉱ 추가 대조로 확정 — 설계서 수량·소수자리와 금액 단위표가 일치하며 both에서는 산림표 우선",
"status": "확정"
},
{
"forest_key": "",
"const_key": "",
"branch": "both",
"evidence": "산림 1-2-5는 '노임'으로 시중노임단가 적용만 규정(L768~770); 건설 공통 1-2-5 '인력'은 직종 선정·작업반장·안전인력을 규정(L260~263)",
"difference_note": "㉯ 이름은 닮았으나 내용이 다름 — 산림의 노임 단가 규정과 건설의 인력 편성 규정이라 절 대응 불가",
"status": "미판정"
},
{
"forest_key": "FW-00401",
"const_key": "CW-00267",
"branch": "both",
"evidence": "산림 12-27-1 지수판 설치 원문 L6731~6742; 건설 공통 6-3-9 지수판 설치 원문 L472~489",
"difference_note": "㉱ 추가 대조로 확정 — 두 표 모두 m당 특별인부 0.151인·보통인부 0.116인, PVC 지수판 1.04m·용접봉 0.042kg·철선 0.21kg, 공구손료 3%로 일치",
"status": "확정"
},
{
"forest_key": "FW-00009",
"const_key": "CW-00010",
"branch": "both",
"evidence": "산림 1-2-3 토질 원문 L665~750; 건설 공통 1-2-3 토질 원문 L99~185",
"difference_note": "㉱ 추가 대조로 확정 — 지반설계, 토질·암 분류와 체적환산계수의 항목·값이 일치하며 both에서는 산림표 우선",
"status": "확정"
},
{
"forest_key": "",
"const_key": "",
"branch": "both",
"evidence": "산림 12-2 표면 마무리 원문 L6157~6165; 건설 공통 6-1-3 표면 마무리 원문 L12~18",
"difference_note": "㉰ 축이 달라 못 이음 — 작업 설명과 숫자 0.34인은 같지만 산림은 ㎡당, 건설은 100㎡당이라 동일 면적 환산 시 100배 차이. 원문 단위 불일치가 해소되기 전 연결 금지",
"status": "미판정"
},
{
"forest_key": "FW-00013",
"const_key": "CW-00014",
"branch": "const_only",
"evidence": "산림 1-2-7 원문 L844가 건설 공통 1-2-7 운반·지게운반을 직접 지시",
"difference_note": "산림 운반 계산에서 건설 지게운반 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00013",
"const_key": "CW-00338",
"branch": "const_only",
"evidence": "산림 1-2-7 원문 L843~844가 건설 공통 8-2-21 경운기의 적재·적하시간과 속도를 직접 지시",
"difference_note": "경운기 운반의 적재·적하시간과 속도를 건설품셈으로 보완",
"status": "확정"
},
{
"forest_key": "FW-00078",
"const_key": "CW-00352",
"branch": "const_only",
"evidence": "산림 2-2-11 원문 L1720이 건설 공통 8-3-1 [00] 토공기계를 직접 지시",
"difference_note": "굴착기 손료를 건설품셈으로 보완",
"status": "확정"
},
{
"forest_key": "FW-00078",
"const_key": "CW-00359",
"branch": "const_only",
"evidence": "산림 2-2-11 원문 L1721이 건설 공통 8-3-8 [70] 기타기계를 직접 지시",
"difference_note": "부착용집게 손료를 건설품셈으로 보완",
"status": "확정"
},
{
"forest_key": "FW-00078",
"const_key": "CW-00354",
"branch": "const_only",
"evidence": "산림 2-2-11 원문 L1722~1723이 건설 공통 8-3-3 [20] 운반 및 하역기계를 직접 지시",
"difference_note": "트럭탑재형·타이어형 크레인 손료를 건설품셈으로 보완",
"status": "확정"
},
{
"forest_key": "FW-00144",
"const_key": "CW-01225",
"branch": "const_only",
"evidence": "산림 5-22-4 원문 L2740이 건설 유지관리 1-2-5 관목 전정을 직접 지시",
"difference_note": "관목 전정 품을 건설품셈으로 보완",
"status": "확정"
},
{
"forest_key": "FW-00150",
"const_key": "CW-00196",
"branch": "const_only",
"evidence": "산림 5-24-1 원문 L2788이 2025 건설 공통 4-1-2 종자뿜어붙이기를 직접 지시; 2026판 4-1-3",
"difference_note": "판 개정으로 절 번호가 4-1-2에서 4-1-3으로 이동",
"status": "확정"
},
{
"forest_key": "FW-00150",
"const_key": "CW-01228",
"branch": "const_only",
"evidence": "산림 5-24-1 원문 L2790이 건설 유지관리 1-2-8 인력관수를 직접 지시",
"difference_note": "물주기 필요 시 인력관수 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00150",
"const_key": "CW-01229",
"branch": "const_only",
"evidence": "산림 5-24-1 원문 L2790이 건설 유지관리 1-2-9 살수차관수를 직접 지시",
"difference_note": "물주기 필요 시 살수차관수 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00152",
"const_key": "CW-00198",
"branch": "const_only",
"evidence": "산림 5-25 원문 L2868이 2025 건설 공통 4-1-4 거적덮기를 직접 지시; 2026판 4-1-5",
"difference_note": "판 개정으로 절 번호가 4-1-4에서 4-1-5로 이동",
"status": "확정"
},
{
"forest_key": "FW-00161",
"const_key": "CW-00320",
"branch": "const_only",
"evidence": "산림 5-29 원문 L2950이 건설 공통 8-2-3 굴착기를 직접 지시",
"difference_note": "굴착기 운전 품을 건설품셈으로 보완",
"status": "확정"
},
{
"forest_key": "FW-00258",
"const_key": "CW-00151",
"branch": "const_only",
"evidence": "산림 9-6-1 원문 L4795가 건설 공통 3-3-6 대규모발파 TYPE-Ⅵ [주]⑦·⑧을 직접 지시",
"difference_note": "기계소할의 발파 관련 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00261",
"const_key": "CW-01325",
"branch": "const_only",
"evidence": "산림 9-7-2 원문 L4842가 건설 유지관리 3-1-1 콘크리트구조물 헐기(인력)를 직접 지시",
"difference_note": "인력 헐기 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00261",
"const_key": "CW-01326",
"branch": "const_only",
"evidence": "산림 9-7-2 원문 L4842가 건설 유지관리 3-1-2 콘크리트구조물 헐기(기계)를 직접 지시",
"difference_note": "기계 헐기 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00263",
"const_key": "CW-00330",
"branch": "const_only",
"evidence": "산림 9-8-1 원문 L4871이 건설 공통 8-2-13 대형브레이커를 직접 지시",
"difference_note": "철골재 철거의 대형브레이커 운전 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00263",
"const_key": "CW-01327",
"branch": "const_only",
"evidence": "산림 9-8-1 원문 L4871이 건설 유지관리 3-1-3 철골재 철거(인력)를 직접 지시",
"difference_note": "인력 철거 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00263",
"const_key": "CW-01328",
"branch": "const_only",
"evidence": "산림 9-8-1 원문 L4871이 건설 유지관리 3-1-4 철골재 철거(기계)를 직접 지시",
"difference_note": "기계 철거 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00264",
"const_key": "CW-01327",
"branch": "const_only",
"evidence": "산림 9-8-2 원문 L4890이 건설 유지관리 3-1-3 철골재 철거(인력)를 직접 지시",
"difference_note": "인력 철거 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00264",
"const_key": "CW-01328",
"branch": "const_only",
"evidence": "산림 9-8-2 원문 L4890이 건설 유지관리 3-1-4 철골재 철거(기계)를 직접 지시",
"difference_note": "기계 철거 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00265",
"const_key": "CW-01329",
"branch": "const_only",
"evidence": "산림 9-9 원문 L4903이 건설 유지관리 3-1-5 석축 헐기(인력)를 직접 지시",
"difference_note": "석축 헐기 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00267",
"const_key": "CW-00330",
"branch": "const_only",
"evidence": "산림 9-10-1 원문 L4919가 건설 공통 8-2-13 대형브레이커를 직접 지시",
"difference_note": "브레이커 운전 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00268",
"const_key": "CW-00330",
"branch": "const_only",
"evidence": "산림 9-10-2 원문 L4938이 건설 공통 8-2-13 대형브레이커를 직접 지시",
"difference_note": "브레이커 운전 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00270",
"const_key": "CW-00416",
"branch": "const_only",
"evidence": "산림 9-11-1 원문 L4957이 건설 토목 1-6-6 포장 절단을 직접 지시",
"difference_note": "포장 절단 품을 건설품셈으로 보완",
"status": "확정"
},
{
"forest_key": "FW-00298",
"const_key": "CW-00320",
"branch": "const_only",
"evidence": "산림 9-15-1 원문 L5286이 건설 공통 8-2-3 굴착기를 직접 지시",
"difference_note": "굴착기 운전 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00299",
"const_key": "CW-00318",
"branch": "const_only",
"evidence": "산림 9-15-2 원문 L5308이 건설 공통 8-2-1 불도저를 직접 지시",
"difference_note": "불도저 운전 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00302",
"const_key": "CW-00320",
"branch": "const_only",
"evidence": "산림 9-16-2 원문 L5340이 건설 공통 8-2-3 굴착기를 직접 지시",
"difference_note": "굴착기 운전 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00302",
"const_key": "CW-00326",
"branch": "const_only",
"evidence": "산림 9-16-2 원문 L5340이 건설 공통 8-2-9 롤러를 직접 지시",
"difference_note": "롤러 운전 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00305",
"const_key": "CW-00332",
"branch": "const_only",
"evidence": "산림 9-17-1 원문 L5372가 건설 공통 8-2-15 법면다짐기를 직접 지시",
"difference_note": "법면다짐기 운전 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00306",
"const_key": "CW-00318",
"branch": "const_only",
"evidence": "산림 9-17-2 원문 L5391이 건설 공통 8-2-1 불도저를 직접 지시",
"difference_note": "불도저 운전 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00307",
"const_key": "CW-00318",
"branch": "const_only",
"evidence": "산림 9-18 원문 L5403이 건설 공통 8-2-1 불도저를 직접 지시",
"difference_note": "불도저 운전 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00310",
"const_key": "CW-00165",
"branch": "const_only",
"evidence": "산림 9-19-2 원문 L5448이 건설 공통 3-5-1 절토면 고르기를 직접 지시",
"difference_note": "절토면 고르기 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00310",
"const_key": "CW-00168",
"branch": "const_only",
"evidence": "산림 9-19-2 원문 L5448이 건설 공통 3-6-1 성토면 고르기를 직접 지시",
"difference_note": "성토면 고르기 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00311",
"const_key": "CW-00165",
"branch": "const_only",
"evidence": "산림 9-19-3 원문 L5465가 건설 공통 3-5-1 절토면 고르기를 직접 지시",
"difference_note": "절토면 고르기 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00311",
"const_key": "CW-00168",
"branch": "const_only",
"evidence": "산림 9-19-3 원문 L5465가 건설 공통 3-6-1 성토면 고르기를 직접 지시",
"difference_note": "성토면 고르기 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00313",
"const_key": "CW-00320",
"branch": "const_only",
"evidence": "산림 9-20-1 원문 L5499가 건설 공통 8-2-3 굴착기를 직접 지시",
"difference_note": "굴착기 운전 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00316",
"const_key": "CW-00320",
"branch": "const_only",
"evidence": "산림 9-22 원문 L5545가 건설 공통 8-2-3 굴착기를 직접 지시",
"difference_note": "굴착기 운전 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00344",
"const_key": "CW-00318",
"branch": "const_only",
"evidence": "산림 10-11 원문 L5920이 건설 공통 8-2-1 불도저를 직접 지시",
"difference_note": "불도저 운전 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00356",
"const_key": "CW-00324",
"branch": "const_only",
"evidence": "산림 11-4 원문 L6080이 건설 공통 8-2-7 모터그레이더를 직접 지시",
"difference_note": "모터그레이더 운전 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00373",
"const_key": "CW-00244",
"branch": "const_only",
"evidence": "산림 12-9-2 원문 L6325가 건설 공통 6-1-1 레디믹스트콘크리트 타설을 직접 지시",
"difference_note": "레디믹스트콘크리트 타설 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00377",
"const_key": "CW-00538",
"branch": "const_only",
"evidence": "산림 12-11-1 원문 L6374가 건설 토목 6-6 원심력 철근콘크리트관 부설을 직접 지시",
"difference_note": "원심력 철근콘크리트관 부설 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00377",
"const_key": "CW-00539",
"branch": "const_only",
"evidence": "산림 12-11-1 원문 L6374가 건설 토목 6-6-1 소켓관 접합을 직접 지시",
"difference_note": "소켓관 접합 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00429",
"const_key": "CW-00320",
"branch": "const_only",
"evidence": "산림 13-2-3 원문 L7019이 건설 공통 8-2-3 굴착기를 직접 지시",
"difference_note": "굴착기 운전 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00445",
"const_key": "CW-00320",
"branch": "const_only",
"evidence": "산림 13-6-1 원문 L7306이 건설 공통 8-2-3 굴착기를 직접 지시",
"difference_note": "굴착기 운전 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00446",
"const_key": "CW-00320",
"branch": "const_only",
"evidence": "산림 13-6-2 원문 L7333이 건설 공통 8-2-3 굴착기를 직접 지시",
"difference_note": "굴착기 운전 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00447",
"const_key": "CW-00320",
"branch": "const_only",
"evidence": "산림 13-6-3 원문 L7359이 건설 공통 8-2-3 굴착기를 직접 지시",
"difference_note": "굴착기 운전 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00449",
"const_key": "CW-00320",
"branch": "const_only",
"evidence": "산림 13-7-1 원문 L7384이 건설 공통 8-2-3 굴착기를 직접 지시",
"difference_note": "굴착기 운전 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00450",
"const_key": "CW-00320",
"branch": "const_only",
"evidence": "산림 13-7-2 원문 L7407이 건설 공통 8-2-3 굴착기를 직접 지시",
"difference_note": "굴착기 운전 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00477",
"const_key": "CW-00320",
"branch": "const_only",
"evidence": "산림 14-2 원문 L7820이 건설 공통 8-2-3 굴착기를 직접 지시",
"difference_note": "굴착기 운전 품을 보완",
"status": "확정"
},
{
"forest_key": "FW-00455",
"const_key": "CW-00339",
"branch": "const_only",
"evidence": "산림 13-10-2 원문 L7511이 건설 공통 8-2-22 디젤파일해머를 직접 지시",
"difference_note": "디젤파일해머 운전 품을 보완",
"status": "확정"
},
{
"forest_key": "",
"const_key": "",
"branch": "const_only",
"evidence": "산림 2-2-9 원문 L1702; 건설 공통 8-3 기계손료표 확인",
"difference_note": "㉮ 건설품셈에 그 규격이 안 실림 — 산림은 1톤 방제차량을 지시하지만 건설 8-3 손료표는 덤프트럭 2.5톤 이상만 수록한다. 8-1-2 [주]④의 개별 산정 대상이며 단일 CW 열쇠로 연결하지 않음",
"status": "미판정"
},
{
"forest_key": "",
"const_key": "",
"branch": "const_only",
"evidence": "산림 9-7-1 원문 L4810",
"difference_note": "㉮ 원문이 절을 안 밝힘 — 건설표준품셈 토목을 적용하라고만 하고 절 번호를 밝히지 않음",
"status": "미판정"
},
{
"forest_key": "",
"const_key": "",
"branch": "const_only",
"evidence": "산림 9-12-1 원문 L4988",
"difference_note": "㉱ 구판-현행 절 대응 미확인 — 2018 건설 토목 3-1-3을 밝히지만 현행 2026판 대응 절을 원문만으로 확정할 수 없음",
"status": "미판정"
},
{
"forest_key": "FW-00070",
"const_key": "CW-00314",
"branch": "const_only",
"evidence": "산림 2-2-3 원문 L1645가 세부 손료에 건설 공통 8-1-5 기계경비 용어와 정의를 준용; 건설 합본 L5324",
"difference_note": "상각비율·정비비율·관리비율 정의를 건설품셈으로 보완",
"status": "확정"
},
{
"forest_key": "FW-00070",
"const_key": "CW-00315",
"branch": "const_only",
"evidence": "산림 2-2-3 원문 L1645가 세부 손료에 건설 공통 8-1-6 기계경비 적산요령을 준용; 건설 합본 L5324 이후",
"difference_note": "시간당 손료계수 산정과 1일 운전시간 원칙을 건설품셈으로 보완",
"status": "확정"
},
{
"forest_key": "FW-00085",
"const_key": "CW-00320",
"branch": "const_only",
"evidence": "산림 3-4-2 원문 L1796이 굴착기 0.2㎥ 시간당 작업량을 건설 공통 8-2-3에 따라 산출하도록 지시; 건설 합본 L5446",
"difference_note": "굴착기 작업능력 Q식을 건설품셈으로 보완",
"status": "확정"
},
{
"forest_key": "FW-00085",
"const_key": "CW-00352",
"branch": "const_only",
"evidence": "산림 3-4-2 원문 L1796이 굴착기 0.2㎥ 시간당 경비를 건설 공통 8-3-1에 따라 산출하도록 지시",
"difference_note": "굴착기 0.2㎥ 손료계수를 건설품셈으로 보완",
"status": "확정"
},
{
"forest_key": "FW-00085",
"const_key": "CW-00363",
"branch": "const_only",
"evidence": "산림 3-4-2 원문 L1796이 굴착기 0.2㎥ 시간당 경비를 건설 공통 8-4-1에 따라 산출하도록 지시",
"difference_note": "굴착기 0.2㎥ 운전경비를 건설품셈으로 보완",
"status": "확정"
},
{
"forest_key": "FW-00124",
"const_key": "CW-00158",
"branch": "const_only",
"evidence": "산림 5-12 원문 L2504가 성토면 흙다짐 필요 시 건설 공통 3-4-3 흙 다지기를 적용하도록 지시; 건설 합본 L2331",
"difference_note": "콤팩터·래머·인력 흙다지기 품을 건설품셈으로 보완",
"status": "확정"
},
{
"forest_key": "FW-00130",
"const_key": "CW-00014",
"branch": "const_only",
"evidence": "산림 5-16-2 원문 L2594가 200m 초과 떼 운반에 건설 공통 1-2-7 제3호 지게운반을 적용하도록 지시; 건설 합본 L453",
"difference_note": "200m 초과 지게운반의 운반량·속도·시간을 건설품셈으로 보완",
"status": "확정"
},
{
"forest_key": "FW-00339",
"const_key": "CW-00325",
"branch": "const_only",
"evidence": "산림 10-9 원문 L5857이 4륜구동 1톤 덤프트럭 작업량에 건설 공통 8-2-8을 적용하도록 지시; 건설 합본 L5648",
"difference_note": "덤프트럭 작업량 Q식을 건설품셈으로 보완",
"status": "확정"
},
{
"forest_key": "FW-00339",
"const_key": "CW-00352",
"branch": "const_only",
"evidence": "산림 10-9 원문 L5857이 손료 산정에 건설 공통 8-3-1의 2.5톤 덤프트럭을 적용하도록 지시",
"difference_note": "2.5톤 덤프트럭 손료계수를 대용",
"status": "확정"
},
{
"forest_key": "FW-00339",
"const_key": "CW-00363",
"branch": "const_only",
"evidence": "산림 10-9 원문 L5857이 운전경비 산정에 건설 공통 8-4-1의 2.5톤 덤프트럭을 적용하도록 지시",
"difference_note": "2.5톤 덤프트럭 운전경비와 주연료 80%를 적용",
"status": "확정"
},
{
"forest_key": "FW-00340",
"const_key": "CW-00014",
"branch": "const_only",
"evidence": "산림 10-9-1 원문 L5871이 표에 규격이 없는 재료에 건설 공통 1-2-7 제7호 화물자동차 적재량을 적용하도록 지시; 건설 합본 L489",
"difference_note": "산림표에 없는 재료의 1회 표준 적재량을 건설품셈으로 보완",
"status": "확정"
}
]
}
@@ -0,0 +1,112 @@
{
"schema_version": "1.0",
"dataset_id": "work_item_axis_policy",
"effective_date": "2026-01-01",
"note": "공종 축의 줄 구실(axis_role) 잣대. 코드에 박지 않고 여기 둔다 — 기관이 정하는 범위라 시스템관리자가 고칠 값이다.",
"policy": {
"never_delete": true,
"why": "표 없는 마디 가운데 상당수가 `parent_mode`(계산 규칙)를 지니고, 총칙 장에는 원가가 실제로 읽는 값표가 붙어 있다. 지우면 계산 규칙과 값이 함께 사라진다(2026-09-17 산림에서 확인 · 브레인 승인).",
"flat_list_filter": "pum_tables > 0",
"flat_list_why": "「잎」으로 거르면 부모 마디에 붙은 표가 통째로 안 뜬다(산림 5건)."
},
"roles": {
"work_item": {
"name_ko": "공종",
"rule": "품셈 표가 붙은 줄",
"keep": true,
"list_label": "공종",
"hidden_by_default": false
},
"group": {
"name_ko": "묶는 마디",
"rule": "표는 없고 아래를 묶는 줄",
"keep": true,
"why": "`parent_mode`(choose_one·sum_steps)가 여기 붙는다 — 계산 규칙을 지닌 줄이다.",
"list_label": "묶는 마디",
"hidden_by_default": true
},
"general_provision": {
"name_ko": "총칙",
"rule": "`general_provision` 뿌리 아래의 줄",
"keep": true,
"why": "문서 구역이나 값표가 붙어 원가가 읽는다.",
"list_label": "공종 아님 · 규정·기준",
"hidden_by_default": true
},
"empty": {
"name_ko": "빈 잎",
"rule": "표도 아래도 없는 잎",
"keep": true,
"why": "원문에 표가 안 붙은 자리. 지우면 「있었는지 없었는지」가 안 남는다.",
"list_label": "빈 잎",
"hidden_by_default": true
}
},
"decision_order": [
"general_provision",
"work_item",
"group",
"empty"
],
"axes": {
"forest": {
"name_ko": "산림 공종",
"settled": true,
"source": "resources/data_cost_input_value/pum_forest_2026.json",
"toc_source": "목차표 F0001 — 원문이 목차를 표로 싣는다",
"code_prefix": "FP",
"key_prefix": "FW",
"general_provision_roots": [
"FP-01",
"FP-02"
],
"general_provision_basis": "FP-01 적용기준 51줄 · FP-02 소요재료 및 기계손료 27줄. ⚠ 지우면 안 됨 — `B09_Estimation_Consumables.py` 가 FP-02-01-01·FP-02-01-08(연료 F0042·F0043·F0058)과 FP-02-02-01/02/04/05/06(손료계수 F0064~F0069)을 읽는다.",
"counts": {
"rows": 477,
"work_item": 304,
"group": 82,
"general_provision": 78,
"empty": 13
},
"known_defects": [
"갈래 문자열이 그대로 줄 열쇠가 됨(`@id` = `FW-00105#1.1∼1.5`) — 품셈 원문이 물결표를 두 가지로 쓰고(∼ U+223C · ~ U+FF5E) 자간 공백만 다른 짝도 있어, 문자열이 손질되면 열쇠가 갈린다. ⬜ 갈래도 장부에 올릴지 판정 대기(2026-09-17 데스크탑 서브)."
]
},
"const": {
"name_ko": "건설 공종",
"settled": true,
"source": "resources/data_cost_input_value/pum_const_2026.json",
"toc_source": "합본 원문 머리 「목 차」 글줄 — 목차표가 없어 랩탑 메인이 글에서 읽음(부문 5 · 장 45 · 1,367줄)",
"code_prefix": "CP",
"key_prefix": "CW",
"code_shape": "CP-<부문2>-<장2>-<절…>",
"code_shape_why": "⚠ **부문마다 장 번호가 1부터 다시 시작한다** — 공통 1장은 적용기준, 토목 1장은 도로포장공사다. 부문 자리가 없으면 부문이 다른 공종끼리 덮는다(산림에서 목차 번호 겹침 둘이 실제로 덮고 있었다).",
"general_provision_roots": [
"01_공통부문/제1장_적용기준.md",
"01_공통부문/제8장_건설기계.md"
],
"general_provision_basis": "공통부문 제1장 적용기준(`CP-01-01`) 줄 37 · 표 46 — 산림 FP-01 과 짝. 공통부문 제8장 건설기계(`CP-01-08`) 줄 75 · 표 351 — 산림 FP-02(소요재료 및 기계손료)와 짝. 시공능력·손료 파라미터라 값을 정하는 자리다(C0426·C0427 은 이미 기초데이터로 꺼냈다). 2026-09-17 안티그래비티 조사 · 브레인 확정.",
"general_provision_pending": [],
"known_defects": [
"형태 미판정 881 / 2,192 — B09 표 읽기가 산림 표로만 검증됨",
"밑수 미확보 447 — 곱하면 안 되는 줄이라 받는 쪽이 가려야 함",
"갈래 키 비어 있음 — 랩탑 메인이 뒤로 미룸"
],
"roots_are": "**장 파일 이름**(「01_공통부문/제1장_적용기준.md」). 뽑는 코드가 목차 코드(`CP-01-01`)로 바꿔 준다 — 관리자가 부문 번호를 셀 필요가 없다.",
"counts": {
"rows": 1367,
"work_item": 997,
"group": 270,
"general_provision": 0,
"empty": 100,
"note": "총칙 표시 전 셈(랩탑 메인 첫 산출). 뿌리를 붙이면 `general_provision` 으로 옮겨 간다 — 줄이 줄지는 않는다."
},
"settled_note": "유지관리부문 제1장 「공 통」(`CP-05-01`) 표 40 은 **공종**으로 둔다 — 총칙이 아니다(2026-09-17 안티그래비티 조사 · 브레인 확정)."
}
},
"list_default": {
"filter": "axis_role",
"value": "work_item",
"why": "목록 기본은 **공종만**(산림 306 · 건설 983). 총칙 줄은 표가 붙어 있어 목록에 섞여 뜨는데, 공종이 아니라 규정·기준이다. 상자를 따로 만들지 않고 **기본 감춤 + 고르면 보임**으로 간다 (2026-09-17 브레인). ⚠ 지운 것이 아니라 감춘 것이다."
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,204 @@
{
"schema_version": "1.0",
"dataset_id": "oil_regional",
"effective_date": "2026-09-09",
"generated_at": "2026-09-09T21:11:38+09:00",
"note": "시도별 유가 — 품셈 8-1-7 5호 「유류가격은 해당지역의 가격으로 한다」. 코드 00(전국)은 원문 그대로 두되 지역 선택지에서는 전국평균 판이 맡는다. ⚠ 원천이 시도를 17줄로 주는데 그 가름이 행정구역과 다르다 — 코드 20 이 「전남광주」 한 줄이고 07(전남)·16(광주)은 아예 없다. 원문 이름을 그대로 두고 우리가 쪼개지 않는다.",
"sources": [
{
"path": "resources/knowledge/original/원가계산/유가_오피넷/유가_시도별_2026-09-09.json",
"sha256": "6f1b58101fbe57be402d44582763ca1d54aea8d9bb5ee9b85b38bdeea2d4e439",
"role": "primary"
}
],
"variables": {
"oil_gasoline": {
"unit": "KRW/L",
"scope": "regional",
"date": "2026-09-09",
"source_product_code": "B027",
"source_product_name": "휘발유",
"records": [
{
"sido_code": "00",
"sido_name": "전국",
"value": 1858.93
},
{
"sido_code": "01",
"sido_name": "서울",
"value": 1905.02
},
{
"sido_code": "02",
"sido_name": "경기",
"value": 1855.78
},
{
"sido_code": "03",
"sido_name": "강원",
"value": 1870.23
},
{
"sido_code": "04",
"sido_name": "충북",
"value": 1866.12
},
{
"sido_code": "05",
"sido_name": "충남",
"value": 1864.78
},
{
"sido_code": "06",
"sido_name": "전북",
"value": 1859.7
},
{
"sido_code": "08",
"sido_name": "경북",
"value": 1852.71
},
{
"sido_code": "09",
"sido_name": "경남",
"value": 1854.71
},
{
"sido_code": "10",
"sido_name": "부산",
"value": 1842.66
},
{
"sido_code": "11",
"sido_name": "제주",
"value": 1889.27
},
{
"sido_code": "14",
"sido_name": "대구",
"value": 1831.78
},
{
"sido_code": "15",
"sido_name": "인천",
"value": 1845.64
},
{
"sido_code": "17",
"sido_name": "대전",
"value": 1836.79
},
{
"sido_code": "18",
"sido_name": "울산",
"value": 1838.49
},
{
"sido_code": "19",
"sido_name": "세종",
"value": 1855.8
},
{
"sido_code": "20",
"sido_name": "전남광주",
"value": 1862.28
}
]
},
"oil_diesel": {
"unit": "KRW/L",
"scope": "regional",
"date": "2026-09-09",
"source_product_code": "D047",
"source_product_name": "자동차용경유",
"records": [
{
"sido_code": "00",
"sido_name": "전국",
"value": 1843.79
},
{
"sido_code": "01",
"sido_name": "서울",
"value": 1886.15
},
{
"sido_code": "02",
"sido_name": "경기",
"value": 1838.22
},
{
"sido_code": "03",
"sido_name": "강원",
"value": 1859.9
},
{
"sido_code": "04",
"sido_name": "충북",
"value": 1851.55
},
{
"sido_code": "05",
"sido_name": "충남",
"value": 1850.08
},
{
"sido_code": "06",
"sido_name": "전북",
"value": 1846.86
},
{
"sido_code": "08",
"sido_name": "경북",
"value": 1838.1
},
{
"sido_code": "09",
"sido_name": "경남",
"value": 1837.71
},
{
"sido_code": "10",
"sido_name": "부산",
"value": 1825.55
},
{
"sido_code": "11",
"sido_name": "제주",
"value": 1869.16
},
{
"sido_code": "14",
"sido_name": "대구",
"value": 1815.72
},
{
"sido_code": "15",
"sido_name": "인천",
"value": 1829.14
},
{
"sido_code": "17",
"sido_name": "대전",
"value": 1824.58
},
{
"sido_code": "18",
"sido_name": "울산",
"value": 1826.63
},
{
"sido_code": "19",
"sido_name": "세종",
"value": 1841.72
},
{
"sido_code": "20",
"sido_name": "전남광주",
"value": 1850.47
}
]
}
}
}
@@ -0,0 +1,31 @@
{
"schema_version": "1.0",
"dataset_id": "oil",
"effective_date": "2026-08-14",
"generated_at": "2026-08-14T19:54:28+09:00",
"sources": [
{
"path": "resources/knowledge/original/원가계산/유가_오피넷/유가_전국평균_2026-08-14.json",
"sha256": "3726397c89108ca5d98b65c0bc615443d02c6fe84794bd95937dabe46464a747",
"role": "primary"
}
],
"variables": {
"oil_gasoline": {
"value": 1863.25,
"unit": "KRW/L",
"scope": "national_average",
"date": "2026-08-14",
"source_product_code": "B027",
"source_product_name": "휘발유"
},
"oil_diesel": {
"value": 1846.39,
"unit": "KRW/L",
"scope": "national_average",
"date": "2026-08-14",
"source_product_code": "D047",
"source_product_name": "자동차용경유"
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,201 @@
{
"schema_version": "1.0",
"dataset_id": "data_master_sources",
"effective_date": "2026-09-15",
"generated_at": "2026-09-16T14:05:40+09:00",
"note": "기초단가 자료가 **어디서 오고 얼마 만에 바뀌는지** 적은 표. 파일 안에는 제 판만 있고 「최신이 무엇인지·어디서 받는지」가 없어 건설 노임이 반 년 뒤처진 것을 아무도 몰랐다(2026-09-15). 그 자리를 메운다.",
"policy": {
"values_untouched": "값을 담지 않는다. 자료가 어디서 오는지만 담는다.",
"our_edition_is_read": "`our_edition` 은 **실제 파일에서 읽는다**(`effective_date`, 파생본은 `derived_from.effective_date`). 손으로 적으면 갱신하고 안 고쳐 또 뒤처진다.",
"address_found_on": "받는 자리 일곱은 2026-09-16 에 안티그래비티가 찾아 채웠다(제조노임·기계·기계 운전경비·자재·유가 전국·유가 지역·요율). 건설노임 한 줄은 그 전(2026-09-15)에 브레인이 준 것이다.",
"no_invented_address": "받는 자리는 **받은 것만** 적는다. 모르면 빈 칸으로 두고 사유를 단다 — 주소를 지어내지 않는다.",
"status_rule": "`latest_published` 가 `our_edition` 보다 뒤면 **뒤처짐**, 같으면 최신, 둘 가운데 하나라도 없으면 **모름**. 날마다 바뀌는 유가는 날짜 차이로 보여야 한다."
},
"kinds": {
"labor": "노임",
"machine": "기계",
"material": "자재",
"oil": "유가",
"rate": "요율"
},
"status": {
"behind": {
"name_ko": "뒤처짐",
"summary": "최신 공표일이 우리 판보다 뒤다 — 받아야 한다."
},
"current": {
"name_ko": "최신",
"summary": "최신 공표일과 우리 판이 같다."
},
"unknown": {
"name_ko": "모름",
"summary": "최신 공표일을 아직 못 잡았다 — 수시·일별이라 한 날로 안 정해지거나, 확인을 아직 안 했다."
}
},
"counts": {
"sources": 8,
"by_status": {
"behind": 1,
"unknown": 4,
"current": 3
}
},
"sources": [
{
"source_id": "labor_const",
"kind": "labor",
"kind_ko": "노임",
"name_ko": "건설업 시중노임단가",
"publisher": "대한건설협회",
"where_to_get": "cak.or.kr [지원·사업] > [건설적산기준] > [건설임금]",
"cycle": "해마다 두 번 — **1월 1일 · 9월 1일**",
"latest_published": "2026-09-01",
"latest_basis": "2026-09-15 브레인 쪽지",
"our_edition": "2026-01-01",
"our_edition_from": "effective_date",
"status": "behind",
"checked_on": "2026-09-15",
"note": "⚠ **뒤처졌다.** 우리 판이 2026-01-01 이라 9월 공표를 반 년 가까이 못 받았다. ⚠ 주기를 **7월 1일로 알고 있던 것이 틀렸다** — 1.1 / 9.1 이다. 노임은 일위대가 품값의 밑값이라 여기가 뒤처지면 **모든 호표가 옛값으로 선다**.",
"files": [
"resources/data_cost_input_value/labor_const_2026-01-01.json"
]
},
{
"source_id": "labor_mfg",
"kind": "labor",
"kind_ko": "노임",
"name_ko": "중소제조업 직종별 임금",
"publisher": "중소기업중앙회",
"where_to_get": "중소기업중앙회 https://www.kbiz.or.kr | [정보마당] > [조사연구통계](게시판 mnSeq=325) > 「중소제조업 직종별 임금조사보고서」 PDF",
"cycle": "반기",
"latest_published": "",
"latest_basis": "",
"our_edition": "2026-07-01",
"our_edition_from": "effective_date",
"status": "unknown",
"checked_on": "2026-09-16",
"note": "우리 판은 2026 상반기 조사(조사월 2026-03 · 공표 2026-06-30)다. ⚠ 게시판에 반기마다 새 보고서가 올라오므로 **최신 공표일은 게시판에서 봐야** 한다.",
"files": [
"resources/data_cost_input_value/labor_mfg_2026-07-01.json"
],
"latest_missing_reason": "수시·일별이라 최신 공표일이 한 날로 안 정해지거나, 아직 확인을 안 했다."
},
{
"source_id": "mach_base",
"kind": "machine",
"kind_ko": "기계",
"name_ko": "건설공사 표준품셈 제8장 건설기계",
"publisher": "국토교통부",
"where_to_get": "공사비원가관리센터 https://cost.kict.re.kr | [표준품셈] (또는 CODIL https://www.codil.or.kr [건설기준정보] > [건설공사표준품셈]) > 건설공사 표준품셈 원문 PDF(공통부문 제8장 건설기계 8-3)",
"cycle": "해마다",
"latest_published": "2026-01-01",
"latest_basis": "2026-09-15 브레인 쪽지",
"our_edition": "2026-01-01",
"our_edition_from": "effective_date",
"status": "current",
"checked_on": "2026-09-16",
"note": "우리 판이 최신이다. 취득가·손료계수·연료소모량·조종원이 다 여기서 온다.",
"files": [
"resources/data_cost_input_value/mach_base_2026.json"
]
},
{
"source_id": "machine_operating",
"kind": "machine",
"kind_ko": "기계",
"name_ko": "건설기계 운전경비(품셈 8-4 파생본)",
"publisher": "국토교통부",
"where_to_get": "공사비원가관리센터 https://cost.kict.re.kr | 별도 배포처 없음(위 건설공사 표준품셈 원문과 같음) > 공통부문 제8장 건설기계 「8-4 건설기계 운전경비 산정」 표",
"cycle": "해마다 — 위 품셈을 따라간다",
"latest_published": "2026-01-01",
"latest_basis": "파생 원본(mach_base)의 판",
"our_edition": "2026-01-01",
"our_edition_from": "derived_from.effective_date",
"status": "current",
"checked_on": "2026-09-16",
"note": "⚠ **판을 모르는 파일이 아니다** — 파일 안 `derived_from.effective_date` 에 2026-01-01 이 박혀 있다(건설품셈 8-4 운전경비 파생본 · 원본 지문까지 함께). `mach_fuel_rate`·`mach_operator_map` 이 다 차면 걷어낼 자리다.",
"files": [
"resources/data_cost_machine_operating/machine_operating_2026.json"
]
},
{
"source_id": "mat_price_public",
"kind": "material",
"kind_ko": "자재",
"name_ko": "관급자재 단가(시설공통자재)",
"publisher": "조달청",
"where_to_get": "나라장터 https://www.g2b.go.kr | [가격정보] > [시설공통자재] (또는 조달청 https://www.pps.go.kr [정보제공] > [업무별자료] > [시설공사]) > 시설공통자재 가격조사 엑셀",
"cycle": "반기 + 수시",
"latest_published": "",
"latest_basis": "",
"our_edition": "2026-08-14",
"our_edition_from": "effective_date",
"status": "unknown",
"checked_on": "2026-09-16",
"note": "수시 공고라 「최신 공표일」이 한 날로 안 정해진다 — 받은 날(우리 판)이 곧 기준이다. 철근·레미콘·아스콘은 이 벌에 없다(쇼핑몰 스냅숏 미확보).",
"files": [
"resources/data_cost_input_value/mat_price_public_2026-08-14.json"
],
"latest_missing_reason": "수시·일별이라 최신 공표일이 한 날로 안 정해지거나, 아직 확인을 안 했다."
},
{
"source_id": "oil",
"kind": "oil",
"kind_ko": "유가",
"name_ko": "유가 — 전국평균",
"publisher": "한국석유공사(오피넷)",
"where_to_get": "오피넷 https://www.opinet.co.kr | [국내유가통계] > [주유소] > [평균판매가격] — 「전국」 탭 조회 뒤 [엑셀저장](API avgAllPrice.do)",
"cycle": "주간 · 일별",
"latest_published": "",
"latest_basis": "",
"our_edition": "2026-08-14",
"our_edition_from": "effective_date",
"status": "unknown",
"checked_on": "2026-09-16",
"note": "날마다 바뀌는 값이라 「최신」이 곧 오늘이다. **뒤처짐을 날짜 차이로 보여야 하는 자료**다. ⚠ 시도별 판과 「전국」 줄이 겹치는데 값도 기준일도 다르다(어느 쪽을 쓸지 판정 대기).",
"files": [
"resources/data_cost_input_value/oil_2026-08-14.json"
],
"latest_missing_reason": "수시·일별이라 최신 공표일이 한 날로 안 정해지거나, 아직 확인을 안 했다."
},
{
"source_id": "oil_regional",
"kind": "oil",
"kind_ko": "유가",
"name_ko": "유가 — 시도별",
"publisher": "한국석유공사(오피넷)",
"where_to_get": "오피넷 https://www.opinet.co.kr | [국내유가통계] > [주유소] > [평균판매가격] — 「지역별」 탭에서 시도를 고른 뒤 [엑셀저장](API avgSidoPrice.do)",
"cycle": "주간 · 일별",
"latest_published": "",
"latest_basis": "",
"our_edition": "2026-09-09",
"our_edition_from": "effective_date",
"status": "unknown",
"checked_on": "2026-09-16",
"note": "품셈 8-1-7 5호 「유류가격은 해당지역의 가격으로 한다」를 따르려고 둔 벌이다.",
"files": [
"resources/data_cost_input_value/oil_regional_2026-09-09.json"
],
"latest_missing_reason": "수시·일별이라 최신 공표일이 한 날로 안 정해지거나, 아직 확인을 안 했다."
},
{
"source_id": "rates",
"kind": "rate",
"kind_ko": "요율",
"name_ko": "토목공사 원가계산 제비율 적용기준",
"publisher": "조달청",
"where_to_get": "조달청 https://www.pps.go.kr | [정보제공] > [업무별자료] > [시설공사] > 「시설공사 원가계산 간접공사비(제비율) 적용기준」 토목 엑셀(재게시 kseis https://www.kseis.co.kr 자료실)",
"cycle": "해마다 + 수시(법이 바뀌면)",
"latest_published": "2026-04-13",
"latest_basis": "2026-09-15 브레인 쪽지",
"our_edition": "2026-04-13",
"our_edition_from": "effective_date",
"status": "current",
"checked_on": "2026-09-16",
"note": "우리 판이 최신이다. 국민연금 요율은 국민연금법(현행 2026-06-17)을 따로 물었다. ⚠ 요율은 **법이 바뀌면 주기와 상관없이** 바뀐다 — 주기만 보고 안심하면 안 된다.",
"files": [
"resources/data_cost_input_value/rates_2026.json"
]
}
]
}
@@ -0,0 +1,45 @@
{
"schema_version": "1.0",
"dataset_id": "fx",
"effective_date": "2026-08-14",
"generated_at": "2026-08-14T19:54:28+09:00",
"sources": [
{
"path": "resources/knowledge/original/원가계산/환율_한국은행ECOS/환율_일별_20260101_2026-08-14.csv",
"sha256": "36a9684af9d6b4a59c3d9c96beec2d75d1d8ad3f01b6af21f078f39fa1d5ffbd",
"role": "primary"
}
],
"variables": {
"fx_usd": {
"value": 1414.9,
"unit": "KRW/USD",
"date": "2026-08-14",
"source_item_code": "0000001"
},
"fx_jpy100": {
"value": 887.33,
"unit": "KRW/JPY100",
"date": "2026-08-14",
"source_item_code": "0000002"
},
"fx_eur": {
"value": 1631.87,
"unit": "KRW/EUR",
"date": "2026-08-14",
"source_item_code": "0000003"
},
"fx_gbp": {
"value": 1908.7,
"unit": "KRW/GBP",
"date": "2026-08-14",
"source_item_code": "0000012"
},
"fx_cny": {
"value": 209.75,
"unit": "KRW/CNY",
"date": "2026-08-14",
"source_item_code": "0000053"
}
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,184 @@
{
"schema_version": "1.0",
"dataset_id": "formwork_reuse",
"effective_date": "2026-01-01",
"note": "거푸집 사용횟수 — **품셈 1-7-1 원문**이 구조물 종류별로 정해 둔 값이다. 관측값이 아니라 법이므로 실무값으로 갈음하지 않는다.",
"source": {
"doc": "산림사업 표준품셈 1-7-1 거푸집 사용",
"table_id": "F0040",
"quote": "2회 T형보, 난간, 특히 복잡한 구조의 교각, 교대, 수문관의 본체 등 복잡한 구조 / 3회 슬래브, 교대, 교각, 옹벽, 파라펫트, 날개벽 등 약간 복잡한 구조 / 4회 측구, 수로, 확대기초, 우물통 등 비교적 간단한 구조 / 6회 수문 또는 관의 기초, 호안 및 보호공의 기초 등 극히 간단한 구조"
},
"policy": {
"b08_delivers": "접촉 면적(㎡) + 사용횟수. **횟수별 재료 환산은 하지 않는다**.",
"b09_applies": "품셈 12-4 의 「사용횟수별 기준수량에 대한 비율(%)」은 일위대가 재료비에 걸린다. B08 이 여기서 곱하면 B09 와 겹쳐 두 번 준다.",
"unlisted_is_flagged": true
},
"reuse_by_class": [
{
"reuse_count": 2,
"class": "복잡한 구조",
"examples": [
"T형보",
"난간",
"복잡한 교각",
"교대",
"수문관 본체"
]
},
{
"reuse_count": 3,
"class": "약간 복잡한 구조",
"examples": [
"슬래브",
"교대",
"교각",
"옹벽",
"파라펫트",
"날개벽"
]
},
{
"reuse_count": 4,
"class": "비교적 간단한 구조",
"examples": [
"측구",
"수로",
"확대기초",
"우물통"
]
},
{
"reuse_count": 6,
"class": "극히 간단한 구조",
"examples": [
"수문 기초",
"관의 기초",
"호안 기초",
"보호공 기초"
]
}
],
"type_map": [
{
"type_id": "retaining_wall",
"reuse_count": 3,
"matched_example": "옹벽",
"note": "원문 3회 줄에 「옹벽」이 그대로 있음"
},
{
"type_id": "pipe_inlet_basin",
"reuse_count": 4,
"matched_example": "거푸집 합판4회",
"note": "그 공종 표가 직접 적은 「거푸집 | 합판4회」 가 정본 — 1-7-1 분류(「호안 및 보호공의 기초」 6회)보다 위(2026-09-14 브레인 ㉯ ②). 앞서 6회로 봤음.",
"basis": "품셈 12-15 집수정 표"
},
{
"type_id": "ford_pavement",
"reuse_count": null,
"note": "물넘이포장은 거푸집이 서지 않는 구조(면 포장) — 대상 아님"
},
{
"type_id": "box_culvert",
"reuse_count": 6,
"matched_example": "관의 기초",
"note": "BOX암거 합판은 **버림 옆면뿐**(본체는 유로폼 — 12-38 유형 「박스」 보통) — 원문 6회 줄 「관의 기초 등 극히 간단한 구조」 · 실무 울진 2×2 「합판6회」 와 같음(2026-09-15)"
}
],
"reuse_ratio_pct": {
"note": "품셈 12-4(표 F0336) 「사용횟수별 기준수량에 대한 비율(%)」. **B09 일위대가가 쓰는 값**이며 B08 은 참고로만 싣는다 — 여기서 곱하면 이중계상. ⚠ 원문은 **「합 판」 한 줄에 두 열**(재료별% · 노무비%)을 적고, 「각 재」 줄에는 비율이 없다.",
"table_id": "F0336",
"plywood_material_pct": {
"1": 100.0,
"2": 57.0,
"3": 46.1,
"4": 40.1,
"5": 37.1,
"6": 34.7
},
"plywood_labor_pct": {
"1": 100.0,
"2": 60.0,
"3": 47.1,
"4": 40.0,
"5": 34.2,
"6": 32.0
},
"timber_absent": {
"why": "⚠ **원문에 각재 비율이 없다.** 앞서 여기 있던 값은 실은 합판 줄의 **노무비(%)** 열을 각재 비율로 잘못 읽은 것이었다(2026-09-18 랩탑 메인 기초값 대조). 그 값은 `plywood_labor_pct` 로 제자리를 찾았고, 각재는 **비워 둔다** — 없는 것을 다른 열로 채우지 않는다.",
"was": {
"1": 100.0,
"2": 60.0,
"3": 47.1,
"4": 40.0,
"5": 34.2,
"6": 32.0
},
"md_row": "「각 재 | ㎥ | 0.038」 — 기준수량만 있고 비율 칸이 비어 있다"
}
},
"shoring": {
"note": "강관동바리(품셈 12-20)는 **슬래브를 떠받칠 때** 필요하다. 옹벽·집수정은 벽체 거푸집만이라 대상이 아니고, BOX암거는 전개식이 동바리 줄을 낸다(2026-09-15).",
"targets_pending": [
"ford_bridge"
],
"why": "세월교는 수량 근거가 없어 슬래브 면적 자체가 안 나온다 — 동바리도 함께 미확보"
},
"euroform_type": {
"note": "유로폼 설치·해체 유형(간단/보통/복잡) — **품셈 12-38-3 [주]④ 원문**이 시설 예시로 갈라 둔다. 거푸집 사용횟수(1-7-1)·철근 갈래(12-3 [주]①)와 같은 자리로, 사람이 고르는 값이 아니다.",
"source": {
"doc": "산림사업 표준품셈 12-38-3 설치 및 해체 [주]④",
"table_id": "F0396",
"quote": "복잡 — 토목: 교대, 날개벽 등 복잡하고 보강이 많은 구조 / 보통 — 측구, 수로, 옹벽, 일반적인 벽체, 박스 등 / 간단 — 수문 또는 관의 기초, 건축 매트기초 등 간단한 구조"
},
"classes": [
{
"key": "복잡",
"examples": [
"교대",
"날개벽"
],
"daily_area_m2": 25
},
{
"key": "보통",
"examples": [
"측구",
"수로",
"옹벽",
"일반적인 벽체",
"박스"
],
"daily_area_m2": 35
},
{
"key": "간단",
"examples": [
"수문 기초",
"관의 기초",
"매트기초"
],
"daily_area_m2": 40
}
],
"type_map": [
{
"type_id": "retaining_wall",
"class": "보통",
"matched": "옹벽"
},
{
"type_id": "box_culvert",
"class": "보통",
"matched": "박스"
},
{
"type_id": "pipe_inlet_basin",
"class": "간단",
"matched": "관의 기초",
"note": "⚠ 집수정 벽체까지 간단으로 볼지는 확인 필요 — 거푸집 사용횟수와 같은 물음"
}
],
"reuse_note": "⚠ 12-38-1 「사용횟수」는 **1-7-1 과 다른 자리다.** 1-7-1 은 합판거푸집처럼 **소모성 거푸집을 몇 번 쓰나**(옹벽 3회)이고, 12-38-1 은 유로폼(강재)의 **잔존율 기준**(12회 사용 잔존율 25 % · 25회 사용 잔존율 10 %)으로 **임대료·손료 산정용**이다. 하나로 잇지 않는다.",
"material_unit_note": "유로폼 자재는 12-38-2 가 **10㎡당 패널 0.89매**로 낸다. B08 은 **접촉 면적(㎡)** 으로 보내고 매수 환산은 그 표의 밑수(10㎡)를 아는 B09 가 한다 — B08 이 환산하면 밑수를 두 벌로 들게 된다."
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,64 @@
{
"schema_version": "1.0",
"dataset_id": "masonry_standard_slope",
"effective_date": "2026-01-01",
"note": "돌쌓기 표준경사 — **직고 · 메/찰 · 성토/절토** 셋으로 갈린다. 우리는 늘 1:0.3 한 값으로 돌고 있었는데, 그것은 이 표의 「메쌓기·성토·직고 1.5m 이하」 한 칸일 뿐이었다(2026-09-09).",
"source": {
"doc": "산림사업 표준품셈(산림청고시 제2025-82호) 13-4-4 [주]⑪ <표 준 경 사>",
"line": "원문 L7182~7194",
"quote": "돌쌓기의 표준경사는 특수한 경우를 제외하고는 쌓기 높이의 최대치를 취하고 표준 뒷길이 상부에서부터 적용한다"
},
"policy": {
"user_override": "저장 제원에 `face_slope_ratio` 가 있으면 **그 값이 이긴다**(사용자 확정 ⑨ 「자동 판정 + 사용자가 바꿀 수 있게」).",
"no_write_back": "⚠ 자동으로 고른 값을 **저장에 박지 않는다.** 「정한 적 없음」과 「0.3 으로 정함」이 구별돼야 한다.",
"boundary": "⚠ 원문 표기는 `∼1.5 · ∼3 · ∼5 · ∼7 · 7이상` 이다. 「∼」를 **이하**로 읽어 경계값은 **앞 칸**에 넣는다(직고 3.0m → `~3` 칸). 7.0m 은 `~7` 칸이고 그보다 커야 `7이상` 칸이다.",
"face_unknown": "⚠ 성토/절토는 **측점 단면유형(`design.section_mode`)과 설치 측으로 판정**한다(2026-09-08 `common_util_structure_face_role` 한 벌로 이음 — 그 전에는 성토 열로 잠정 적용했다). 판정이 안 되는 자리(`both_cut` + 설치 측 「자동」)만 남고, 그때는 **성토로 눅이지 않고** 종전값(1:0.3)으로 서되 **왜 못 갈랐는지**를 근거에 적는다."
},
"steps_m": [
1.5,
3.0,
5.0,
7.0
],
"table": {
"메쌓기": {
"성토": [
0.3,
0.35,
0.4,
0.45,
0.5
],
"절토": [
0.25,
0.3,
0.35,
0.4,
0.45
]
},
"찰쌓기": {
"성토": [
0.25,
0.3,
0.35,
0.4,
0.45
],
"절토": [
0.2,
0.25,
0.3,
0.35,
0.4
]
}
},
"not_here": {
"note": "이 표가 정하지 않는 것.",
"items": [
"⚠ 교본 부록 4-5:752 는 **시설 종류**로 가른다 — 「돌 기슭막이의 돌쌓기 기울기는 찰쌓기 1:0.3, **메쌓기 1:0.5**」. 품셈은 직고·성절토 축이라 **축이 다르다.** 기슭막이로 놓은 벽은 사용자가 덮어쓰기로 처리한다(확정 ⑨).",
"큰돌쌓기(13-6)는 이 표의 대상이 아니다 — 교본이 「1:0.3 이상」으로만 두었다."
]
}
}
@@ -0,0 +1,54 @@
{
"schema_version": "1.0",
"dataset_id": "masonry_class",
"effective_date": "2026-01-01",
"note": "돌쌓기(13-4)·큰돌쌓기(13-6) 의 **규격 갈래**. 저장 제원의 값으로 자동 판정하며 사람이 고르는 값이 아니다.",
"back_length": {
"note": "돌쌓기 — 저장 제원 `back_len_cm`(㎝). 품셈 갈래는 「…㎝ 이하」 구간이라 **저장값 이상인 첫 경계**를 고른다.",
"option_key": "back_len_cm",
"source": "품셈 13-4 뒷길이 표준 · B09 단가 갈래(#35cm이하·#55cm이하·#75cm이하)",
"classes": [
{
"max_cm": 35,
"key": "35cm이하"
},
{
"max_cm": 55,
"key": "55cm이하"
},
{
"max_cm": 75,
"key": "75cm이하"
}
]
},
"boulder_diameter": {
"note": "큰돌쌓기 — 저장 제원 `stone_cm`. **품셈 13-6 직경 축과 글자까지 같다** — 접거나 바꾸지 않고 그대로 쓴다.",
"option_key": "stone_cm",
"source": "품셈 13-6 「직경 40㎝이상∼60㎝미만 / 60㎝이상∼80㎝미만 / 80㎝이상∼100㎝이하」",
"classes": [
"40~60",
"60~80",
"80~100"
]
},
"face_slope": {
"note": "전면 기울기(1:n) — **교본 7-3 돌흙막이 기준**이 형식별로 정해 둔다. 코드의 기본 0.3 은 지어낸 값이 아니라 이 표에서 온 것이다.",
"source": "resources/knowledge/technical_info/01_임도/02_상세설계/구조물/돌쌓기.md §1 (교본 7-3)",
"quote": "돌 찰쌓기 3.0m 이하 1:0.3 / 돌 메쌓기 2.0m 이하 1:0.3 / 큰돌쌓기 1:0.3 이상(전도 방지)",
"by_type": {
"masonry_wet": 0.3,
"masonry_dry": 0.3,
"boulder_masonry": 0.3
},
"pending": "⚠ 큰돌쌓기는 「1:0.3 **이상**」이라 더 완만하게 잡을 수 있음 — 칸을 만든다면 그 범위를 보여야 함. 지금은 하한 0.3 으로 감."
},
"bond": {
"note": "큰돌쌓기 쌓기 방식 — 저장 제원 `bond`. 품셈 13-6-1(메)·13-6-2(찰)로 그대로 갈린다.",
"option_key": "bond",
"codes": {
"메쌓기": "FP-13-06-01",
"찰쌓기": "FP-13-06-02"
}
}
}
@@ -0,0 +1,40 @@
{
"schema_version": "1.0",
"dataset_id": "masonry_back_length_standard",
"effective_date": "2026-01-01",
"note": "돌쌓기 뒷길이 표준 — **높이 × 메/찰** 로 범위가 갈린다. 뒷길이를 안 고른 돌쌓기는 붙박이 45㎝ 로 서고 있었는데, 그 자리를 이 표의 **하한**으로 바꿈(2026-09-14 브레인 판정). 사용자가 칸에서 고르면 그 값이 이김.",
"source": {
"doc": "산림사업 표준품셈(산림청고시 제2025-82호) 13-4-4 [주]⑩ <뒷길이 표준>",
"line": "원문 L7172~7181",
"quote": "높은 메쌓기는 밑으로 내려갈수록 뒷길이를 길게 하는 것이 원칙이다 · 높이는 첨단으로부터 기준 한다 · 특수한 조건에서는 본 기준과 달리 적용할 수 있다"
},
"policy": {
"suggestion": "⭐ 제안값 = **범위 안에 드는 품셈 규격 중 가장 작은 것**(브레인 판정 ㉮). 하한이 규격(25·30·35·45·55·60·75)에 없으면 그 위 첫 규격 — 메쌓기 ~3m·~5m 하한 36㎝ 는 45㎝. 35 로 내리면 품셈 범위 아래로 나가 안 됨.",
"boundary": "⚠ 원문 표기 `∼1.5 · ∼3 · ∼5 · ∼7 · 7이상` — 표준경사 표(`masonry_slope`)와 같이 「∼」를 **이하**로 읽어 경계값은 앞 칸(높이 3.0m → `~3` 칸).",
"no_write_back": "⚠ 제안값을 저장에 박지 않음 — 「안 고름」과 「45 로 고름」이 구별돼야 함(표준경사와 같은 자리)."
},
"steps_m": [1.5, 3.0, 5.0, 7.0],
"table_cm": {
"메쌓기": [
[25, 35],
[36, 45],
[36, 60],
[45, 75],
[75, null]
],
"찰쌓기": [
[25, 35],
[30, 35],
[35, 45],
[35, 55],
[45, 60]
]
},
"not_here": {
"note": "이 표가 쓰지 않는 칸.",
"items": [
"돌붙이기 열(메 25~60 · 찰 20~40)은 높이로 안 갈림 — 바닥막이 돌붙임은 뒷길이가 붙박이(`_UnitQuantity_Revetment.BED_SILL_FORMS`)라 여기서 안 씀",
"골막이는 뒷길이 기본이 종전대로 45㎝ — 이 표의 대상(돌쌓기 13-4)이 아님"
]
}
}
@@ -0,0 +1,80 @@
{
"schema_version": "1.0",
"dataset_id": "timber_structure_class",
"effective_date": "2026-01-01",
"note": "목재공작물 구조 갈래(품셈 13-13-1 [주]③). **재료 구성**으로 가르며 원문이 예시를 든다. 구조물 종류가 늘면 `type_map` 에 줄만 더한다.",
"source": {
"doc": "산림사업 표준품셈 13-13-1 목재틀흙막이 [주]③",
"table_id": "F0440",
"quote": "보통구조 : 통나무나 대각재, 후판 등이 대부분(80%) 이상으로, 목재 채적에 비해 가공정도가 적은 공작물 (하: 통나무 경계목 / 중: 통나무 방풍책 / 상: 통나무 기슭막이) · 중등구조 : 통나무나 대각재, 후판 등이 절반(50%) 이상 (보통: 통나무 바닥막이·누구막이 / 상: 통나무 골막이) · 상등구조 : 소각재, 박판, 소폭판 등이 목재의 50% 이상 (통나무 사방댐 등)"
},
"basis_unit": {
"note": "⚠ 밑수 1㎥ 는 **구조물 체적이 아니라 목재 채적**이다. 원문이 「목재 채적에 비해 가공정도」로 갈래를 가르는 데서 드러난다. 그래야 「1㎥에 건축목공 17인」이 말이 된다.",
"unit": "목재 채적 ㎥"
},
"classes": [
{
"key": "보통구조 하",
"carpenter": 6.285,
"laborer": 0.682,
"examples": [
"통나무 경계목"
]
},
{
"key": "보통구조 중",
"carpenter": 7.274,
"laborer": 0.786,
"examples": [
"통나무 방풍책"
]
},
{
"key": "보통구조 상",
"carpenter": 8.76,
"laborer": 0.958,
"examples": [
"통나무 기슭막이"
]
},
{
"key": "중등구조 보통",
"carpenter": 10.612,
"laborer": 1.156,
"examples": [
"통나무 바닥막이",
"누구막이"
]
},
{
"key": "중등구조 상",
"carpenter": 13.767,
"laborer": 1.497,
"examples": [
"통나무 골막이"
]
},
{
"key": "상등구조",
"carpenter": 16.975,
"laborer": 1.848,
"examples": [
"통나무 사방댐"
]
}
],
"type_map": [
{
"type_id": "soil_guard",
"class": "보통구조 상",
"matched": "통나무 기슭막이",
"provisional": true,
"why": "임도 흙막이는 통나무를 짜 맞춘 틀이고 **소각재·박판·소폭판이 목재의 50 % 이상**이라는 상등구조 조건을 안 채운다. 원문 예시로도 「통나무 기슭막이」가 같은 급이고 「통나무 사방댐」은 사방 구조물이다.",
"compare": "상등구조를 쓰면 건축목공 8.760 → 16.975 인/㎥ 로 약 1.9배"
}
],
"pending_user": {
"note": "⚠ 위 판정은 **잠정**이다. 사용자가 예/아니오로 답할 수 있게 물음을 좁혀 둔다.",
"question": "임도 흙막이의 목재공작물 구조 갈래가 「보통구조 상(통나무 기슭막이 급)」이 맞습니까?"
}
}
@@ -0,0 +1,558 @@
{
"schema_version": "1.0",
"dataset_id": "resource_catalog_ext",
"effective_date": "2026-01-01",
"note": "기존 카탈로그(노임·기계·관급 자재)에 없는 자원의 이름·규격·단위. 단가 칸 없음(명세 §2③·7장 보류). 규격은 조인 키 — 품셈 칸이 규격을 안 주면 매칭 성공이 아님(「규격 미정 — 후보 N」). STmate 코드는 교차 확인용 별칭일 뿐 우리 코드가 아님(명세 §11).",
"policy": {
"code_shape": "AR-<M|L|X>-<8자리 소문자 16진>",
"code_compare": "equality_only",
"spec_is_join_key": true,
"auto_adopt_single_candidate": false,
"no_price": true
},
"entries": [
{
"code": "AR-M-b0853497",
"kind": "material",
"name": "비료",
"spec": "복합비료",
"unit": "kg",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0135",
"F0136"
]
}
},
{
"code": "AR-M-61df616d",
"kind": "material",
"name": "피복제",
"spec": "화이버",
"unit": "kg",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0135",
"F0136"
]
}
},
{
"code": "AR-M-86ddf05f",
"kind": "material",
"name": "침식안정제",
"spec": "합성접착제",
"unit": "kg",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0135",
"F0136"
]
}
},
{
"code": "AR-M-ae538556",
"kind": "material",
"name": "색소",
"spec": "색소",
"unit": "kg",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0135",
"F0136"
]
}
},
{
"code": "AR-M-dce99e46",
"kind": "material",
"name": "말뚝",
"spec": "직경4~6㎝, 길이120㎝ 기준",
"unit": "개",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0120"
]
}
},
{
"code": "AR-M-3f3d8612",
"kind": "material",
"name": "브레이드",
"spec": "320~400mm",
"unit": "개",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0256"
]
}
},
{
"code": "AR-M-369b67f2",
"kind": "material",
"name": "유공관",
"spec": "∅200mm",
"unit": "m",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0345"
]
}
},
{
"code": "AR-M-d2c51025",
"kind": "material",
"name": "접합몰탈",
"spec": "1:2",
"unit": "㎥",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0347"
]
}
},
{
"code": "AR-M-f89c57d4",
"kind": "material",
"name": "종자",
"spec": "생태복원형,초본류",
"unit": "kg",
"source": {
"stmate_codes": [
"M00086",
"M00661"
]
}
},
{
"code": "AR-M-d52b8fc1",
"kind": "material",
"name": "종자",
"spec": "생태복원형,목본류",
"unit": "kg",
"source": {
"stmate_codes": [
"M00087",
"M00671"
]
}
},
{
"code": "AR-M-8f852514",
"kind": "material",
"name": "종자",
"spec": "초본류",
"unit": "kg",
"source": {
"stmate_codes": [
"M01225"
]
}
},
{
"code": "AR-M-e39ee36e",
"kind": "material",
"name": "종자",
"spec": "목본류",
"unit": "kg",
"source": {
"stmate_codes": [
"M01226"
]
}
},
{
"code": "AR-M-6fc2930f",
"kind": "material",
"name": "폭약",
"spec": "28㎜(메가마이트Ⅰ)",
"unit": "kg",
"source": {
"stmate_codes": [
"M00815"
]
}
},
{
"code": "AR-M-0965627d",
"kind": "material",
"name": "뇌관",
"spec": "",
"unit": "개",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0243"
]
}
},
{
"code": "AR-M-a1a18ec4",
"kind": "material",
"name": "비트",
"spec": "",
"unit": "개",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0243"
]
}
},
{
"code": "AR-M-5257ad1e",
"kind": "material",
"name": "철망태",
"spec": "",
"unit": "㎥",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0436"
]
}
},
{
"code": "AR-M-7d8a0fbc",
"kind": "material",
"name": "채움재",
"spec": "",
"unit": "㎥",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0436"
]
}
},
{
"code": "AR-X-79f64ab7",
"kind": "machine",
"name": "종자살포기",
"spec": "2,500-3,000ℓ",
"unit": "시간",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0135",
"F0136"
]
}
},
{
"code": "AR-X-95cb3904",
"kind": "machine",
"name": "착암기",
"spec": "2.7㎥/min",
"unit": "hr",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0243"
]
}
},
{
"code": "AR-M-423cfb79",
"kind": "material",
"name": "고무링",
"spec": "∅800mm",
"unit": "개",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0346"
]
}
},
{
"code": "AR-M-18c0ce85",
"kind": "material",
"name": "고무링",
"spec": "∅1000mm",
"unit": "개",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0346"
]
}
},
{
"code": "AR-M-d9d5aa16",
"kind": "material",
"name": "고무링",
"spec": "∅1200mm",
"unit": "개",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0346"
]
}
},
{
"code": "AR-M-0cc44237",
"kind": "material",
"name": "지수활제",
"spec": "",
"unit": "g",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0346"
]
}
},
{
"code": "AR-M-a2926452",
"kind": "material",
"name": "치즐",
"spec": "대형브레이커용",
"unit": "본",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0241",
"F0244",
"F0259",
"F0260",
"F0267",
"F0268",
"F0269",
"F0270",
"F0271",
"F0272",
"F0273",
"F0274",
"F0275",
"F0276",
"F0277",
"F0278"
],
"stmate_codes": [
"M00118"
]
}
},
{
"code": "AR-M-10aba455",
"kind": "material",
"name": "유로폼 패널",
"spec": "600 x 1,200mm",
"unit": "매",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0393"
]
}
},
{
"code": "AR-M-5b89b294",
"kind": "material",
"name": "유로폼 내부 패널",
"spec": "(200+200) x 1,200mm",
"unit": "매",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0393"
]
}
},
{
"code": "AR-M-d00a6b1a",
"kind": "material",
"name": "유로폼 임대료",
"spec": "㎡당(임대기간 반영 · 설계자 셈)",
"unit": "㎡",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0393"
]
}
},
{
"code": "AR-M-249d0a01",
"kind": "material",
"name": "파쇄기날",
"spec": "이동식 임목파쇄기용",
"unit": "개",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0237"
]
}
},
{
"code": "AR-X-61d1681d",
"kind": "machine",
"name": "체인톱",
"spec": "45cc (배기량기준)",
"unit": "대·일",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0042",
"F0064"
]
}
},
{
"code": "AR-X-da716e54",
"kind": "machine",
"name": "예취기",
"spec": "배기량 35cc",
"unit": "대·일",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0043",
"F0065"
]
}
},
{
"code": "AR-M-148c5888",
"kind": "material",
"name": "예취기 구입가",
"spec": "배기량 35cc · 손료 밑수",
"unit": "대",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0065"
]
}
},
{
"code": "AR-X-5d0a0416",
"kind": "machine",
"name": "배부식분무기",
"spec": "덩굴 약제처리",
"unit": "대·일",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0067"
]
}
},
{
"code": "AR-M-d6394831",
"kind": "material",
"name": "배부식분무기 구입가",
"spec": "덩굴 약제처리 · 손료 밑수",
"unit": "대",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0067"
]
}
},
{
"code": "AR-M-5649cf3f",
"kind": "material",
"name": "체인톱 구입가",
"spec": "45cc (배기량기준) · 손료 밑수",
"unit": "대",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0064"
]
}
},
{
"code": "AR-M-fa7fbf6d",
"kind": "material",
"name": "체인오일",
"spec": "일반오일",
"unit": "ℓ",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0042"
]
}
},
{
"code": "AR-M-de2fe662",
"kind": "material",
"name": "체인오일",
"spec": "친환경오일",
"unit": "ℓ",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0042"
]
}
},
{
"code": "AR-X-ea09a419",
"kind": "machine",
"name": "유인헬기방제 양수기",
"spec": "5HP (유인헬기 방제용)",
"unit": "대·일",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0058",
"F0069"
]
}
},
{
"code": "AR-M-cfc70216",
"kind": "material",
"name": "유인헬기방제 양수기 구입가",
"spec": "5HP · 손료 밑수",
"unit": "대",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0069"
]
}
},
{
"code": "AR-M-a79b4d20",
"kind": "material",
"name": "유인헬기방제 깃발",
"spec": "천재질·코팅·박음마감",
"unit": "개",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0058"
]
}
},
{
"code": "AR-M-08db9ecc",
"kind": "material",
"name": "유인헬기 임차료",
"spec": "1일 1대 · 견적",
"unit": "일",
"source": {
"pum_edition": "2026-01-01",
"pum_table_ids": [
"F0225"
]
}
}
]
}
@@ -0,0 +1,101 @@
{
"schema_version": "1.0",
"dataset_id": "rebar_complexity",
"effective_date": "2026-01-01",
"note": "철근 가공·조립 갈래(간단/보통/복잡/매우복잡) — **품셈 12-3 [주]① 원문**이 구조물을 예시로 갈라 둔다. 사람이 고르는 값이 아니라 법으로 정해지는 값이다(거푸집 사용횟수 1-7-1 과 같은 자리).",
"source": {
"doc": "산림사업 표준품셈 12-3 철근 현장가공 및 조립 [주] ①",
"table_id": "F0335",
"quote": "간단한 것이란 측구, 간단한 기초 및 중력식 옹벽 등을 말하며, 보통의 것이란 수문, 반중력식 옹벽 및 교대 등을 말하고, 복잡한 것이란 교량의 슬래브, 암거, 우물통 부벽식 옹벽 등을 말하며, 매우 복잡한 것이란 구주식(기둥형) 교대, 교각, 지하철, 터널"
},
"policy": {
"auto_decided": true,
"unlisted_is_flagged": true,
"note": "원문 예시에 걸리는 것만 정한다. 안 걸리면 「갈래 미확보」로 드러내고 임의로 고르지 않는다."
},
"classes": [
{
"key": "간단",
"examples": [
"측구",
"간단한 기초",
"중력식 옹벽"
]
},
{
"key": "보통",
"examples": [
"수문",
"반중력식 옹벽",
"교대"
]
},
{
"key": "복잡",
"examples": [
"교량의 슬래브",
"암거",
"우물통",
"부벽식 옹벽"
]
},
{
"key": "매우복잡",
"examples": [
"구주식(기둥형) 교대",
"교각",
"지하철",
"터널"
]
}
],
"form_map": [
{
"type_id": "retaining_wall",
"form": "중력식",
"class": "간단",
"matched": "중력식 옹벽"
},
{
"type_id": "retaining_wall",
"form": "반중력식",
"class": "보통",
"matched": "반중력식 옹벽"
},
{
"type_id": "retaining_wall",
"form": "부벽식",
"class": "복잡",
"matched": "부벽식 옹벽"
},
{
"type_id": "retaining_wall",
"form": "캔틸레버식",
"class": null,
"why": "원문 [주]① 예시에 캔틸레버식 옹벽이 없음 — 임의로 고르지 않고 미확보로 드러냄"
},
{
"type_id": "box_culvert",
"class": "복잡",
"matched": "암거"
},
{
"type_id": "pipe_inlet_basin",
"class": "보통",
"matched": "철근가공조립(보통)",
"basis": "품셈 12-15 집수정 표",
"note": "그 공종 표가 직접 적은 「철근 | 철근가공조립(보통)」 이 정본 — 12-3 [주]① 「간단한 기초」 추정보다 위(2026-09-14 브레인 ㉯ ⑶ · 거푸집 4회와 같은 원칙). 앞서 간단으로 봤음."
}
],
"price_hint_krw_per_ton": {
"note": "⚠ **표시 전용.** 갈래를 자동으로 정하더라도 화면에 갈래 이름과 차이를 보여야 사람이 검증할 수 있다. B08 의 어떤 계산에도 안 들어간다(금액은 B09 몫).",
"computed_by": "B09",
"computed_on": "2026-09-07",
"values": {
"간단": 919146.7,
"보통": 1032497.4,
"복잡": 1143569.7,
"매우복잡": 1278375.3
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,387 @@
{
"schema_version": "1.0",
"dataset_id": "machine_productivity",
"effective_date": "2026-01-01",
"note": "기계 작업량 밑값 — 코드에 박혀 있던 값을 그대로 옮긴 벌(2026-09-16 사용자 원칙 「마스터 = 관리자가 제어할 값 · 코드 안에 있으면 안 됨」). 값은 한 칸도 안 바꿨다 — 옮기기 전후 금액이 같다.",
"policy": {
"no_value_changed": "코드에 있던 값을 그대로 옮겼다. 원문과의 대조는 시험이 지킨다.",
"formula_stays_in_code": "식(Q = n·q·f·E 따위)은 값이 아니라 로직이라 코드에 둔다 — 계수만 여기 있다.",
"source_tables_mashed": "삽날 용량표는 원문 옮기기에서 절 이름이 뭉개져(`5-3.2-5.5-타 이 어…`) 표로 안 들어왔다. 값은 원문 본문 것이고, 원문 표가 제 모양으로 들어오면 그때 대조한다."
},
"sources": [
{
"doc": "건설공사 표준품셈 8-2-1 불도저 2.가·나 전진·후진 속도",
"tables": [
"C0426",
"C0427"
],
"role": "primary"
},
{
"doc": "건설공사 표준품셈 8-2-1 1.가 삽날 용량 q˚",
"tables": [],
"role": "primary",
"note": "원문 표가 뭉개져 카탈로그에 표로 없음"
},
{
"doc": "산림사업 표준품셈(고시 제2025-82호) 10-12 「1. 적재」·「2. 운반」 식 서식",
"tables": [],
"role": "primary",
"note": "원문이 표가 아니라 채워 넣는 식이라 공종 마스터에 안 실림"
}
],
"variables": {
"dozer_speed": {
"unit": "m/분",
"key": "track+tonnage_ton+gear",
"note": "후진이 「-」인 단은 원문에 값이 없어 담지 않는다(없는 속도로 싸이클이 서면 값이 조용히 틀림).",
"records": [
{
"track": "무한궤도",
"tonnage_ton": "4",
"gear": 1,
"forward_m_per_min": "40",
"reverse_m_per_min": "63"
},
{
"track": "무한궤도",
"tonnage_ton": "4",
"gear": 2,
"forward_m_per_min": "57",
"reverse_m_per_min": "85"
},
{
"track": "무한궤도",
"tonnage_ton": "7",
"gear": 1,
"forward_m_per_min": "43",
"reverse_m_per_min": "53"
},
{
"track": "무한궤도",
"tonnage_ton": "7",
"gear": 2,
"forward_m_per_min": "67",
"reverse_m_per_min": "78"
},
{
"track": "무한궤도",
"tonnage_ton": "7",
"gear": 3,
"forward_m_per_min": "92",
"reverse_m_per_min": "107"
},
{
"track": "무한궤도",
"tonnage_ton": "10",
"gear": 1,
"forward_m_per_min": "42",
"reverse_m_per_min": "50"
},
{
"track": "무한궤도",
"tonnage_ton": "10",
"gear": 2,
"forward_m_per_min": "64",
"reverse_m_per_min": "75"
},
{
"track": "무한궤도",
"tonnage_ton": "10",
"gear": 3,
"forward_m_per_min": "88",
"reverse_m_per_min": "105"
},
{
"track": "무한궤도",
"tonnage_ton": "12",
"gear": 1,
"forward_m_per_min": "40",
"reverse_m_per_min": "48"
},
{
"track": "무한궤도",
"tonnage_ton": "12",
"gear": 2,
"forward_m_per_min": "55",
"reverse_m_per_min": "70"
},
{
"track": "무한궤도",
"tonnage_ton": "12",
"gear": 3,
"forward_m_per_min": "75",
"reverse_m_per_min": "100"
},
{
"track": "무한궤도",
"tonnage_ton": "13",
"gear": 1,
"forward_m_per_min": "40",
"reverse_m_per_min": "48"
},
{
"track": "무한궤도",
"tonnage_ton": "13",
"gear": 2,
"forward_m_per_min": "55",
"reverse_m_per_min": "70"
},
{
"track": "무한궤도",
"tonnage_ton": "19",
"gear": 1,
"forward_m_per_min": "40",
"reverse_m_per_min": "46"
},
{
"track": "무한궤도",
"tonnage_ton": "19",
"gear": 2,
"forward_m_per_min": "55",
"reverse_m_per_min": "70"
},
{
"track": "무한궤도",
"tonnage_ton": "19",
"gear": 3,
"forward_m_per_min": "75",
"reverse_m_per_min": "98"
},
{
"track": "무한궤도",
"tonnage_ton": "32",
"gear": 1,
"forward_m_per_min": "40",
"reverse_m_per_min": "43"
},
{
"track": "무한궤도",
"tonnage_ton": "32",
"gear": 2,
"forward_m_per_min": "52",
"reverse_m_per_min": "58"
},
{
"track": "무한궤도",
"tonnage_ton": "32",
"gear": 3,
"forward_m_per_min": "70",
"reverse_m_per_min": "78"
},
{
"track": "타이어",
"tonnage_ton": "15",
"gear": 1,
"forward_m_per_min": "83",
"reverse_m_per_min": "92"
},
{
"track": "타이어",
"tonnage_ton": "15",
"gear": 2,
"forward_m_per_min": "200",
"reverse_m_per_min": "125"
},
{
"track": "타이어",
"tonnage_ton": "28",
"gear": 1,
"forward_m_per_min": "92",
"reverse_m_per_min": "92"
},
{
"track": "타이어",
"tonnage_ton": "28",
"gear": 2,
"forward_m_per_min": "200",
"reverse_m_per_min": "200"
},
{
"track": "타이어",
"tonnage_ton": "33",
"gear": 1,
"forward_m_per_min": "92",
"reverse_m_per_min": "110"
},
{
"track": "타이어",
"tonnage_ton": "33",
"gear": 2,
"forward_m_per_min": "210",
"reverse_m_per_min": "250"
}
]
},
"dozer_blade": {
"unit": "㎥",
"key": "track+tonnage_ton",
"note": "무한궤도 10·13 톤이 둘 다 1.5 ㎥ 라 용량만으로는 못 가른다 — 속도로 마저 가른다.",
"records": [
{
"track": "무한궤도",
"tonnage_ton": "4",
"blade_m3": "0.5"
},
{
"track": "무한궤도",
"tonnage_ton": "7",
"blade_m3": "1.1"
},
{
"track": "무한궤도",
"tonnage_ton": "10",
"blade_m3": "1.5"
},
{
"track": "무한궤도",
"tonnage_ton": "12",
"blade_m3": "2.0"
},
{
"track": "무한궤도",
"tonnage_ton": "13",
"blade_m3": "1.5"
},
{
"track": "무한궤도",
"tonnage_ton": "19",
"blade_m3": "3.2"
},
{
"track": "무한궤도",
"tonnage_ton": "32",
"blade_m3": "5.5"
},
{
"track": "타이어",
"tonnage_ton": "15",
"blade_m3": "3.1"
},
{
"track": "타이어",
"tonnage_ton": "28",
"blade_m3": "4.0"
},
{
"track": "타이어",
"tonnage_ton": "33",
"blade_m3": "5.7"
}
]
},
"dump_haul": {
"key": "key",
"note": "덤프 운반·적재 식이 쓰는 밑값. 장비 규격이 바뀌면 여기를 고친다.",
"records": [
{
"key": "truck_ton",
"value": "15",
"unit": "ton",
"meaning": "덤프트럭 적재 규격 T"
},
{
"key": "bucket_m3",
"value": "0.7",
"unit": "㎥",
"meaning": "적재기계 버켓 용량 q"
},
{
"key": "loader_cycle_sec",
"value": "20",
"unit": "초",
"meaning": "적재 대기 싸이클 ㎝s"
},
{
"key": "speed_loaded_kmh",
"value": "5",
"unit": "㎞/hr",
"meaning": "적재 주행속도 V1"
},
{
"key": "speed_empty_kmh",
"value": "6",
"unit": "㎞/hr",
"meaning": "공차 주행속도 V2"
},
{
"key": "unload_min",
"value": "1.1",
"unit": "분",
"meaning": "적하 시간 t3"
},
{
"key": "wait_min",
"value": "0.9",
"unit": "분",
"meaning": "대기 시간 t4"
},
{
"key": "cover_min",
"value": "0.5",
"unit": "분",
"meaning": "덮개 시간 t5"
},
{
"key": "loading_cycle_sec",
"value": "22",
"unit": "초",
"meaning": "적재 식 싸이클 ㎝(180°)"
}
]
},
"dump_material": {
"key": "work_item_code",
"note": "10-12 절 재료별 값 — 고른 값·버린 값 사유를 줄에 함께 둔다. 적재 사유는 `loading_note` 로 따로 둔다(운반 줄 사유와 섞지 않음).",
"records": [
{
"work_item_code": "FP-10-12-01",
"label": "토사",
"unit_weight_ton_per_m3": "1.9",
"loose_factor": "1.3",
"bucket_factor": "0.9",
"loader_efficiency": "0.85",
"truck_efficiency": "0.9",
"loading_efficiency": "0.60",
"notes": [
"n 의 Qt = 계산값 T/γt×L(원문 표기 「10/(0.7×K)」의 10 은 토사 어림수 복사로 봄 — 버림)",
"Es = 운반 줄 값(적재 식의 E0 와 다른 기호)"
],
"loading_note": "E0 = 0.60(표 「임도」·10-12-3 [주]③) — 본문 표기 0.75 버림",
"loading_efficiency_basis": "원문 10-12-1 은 식에 `E0=0.75` 를 적지만, 바로 아래 [주]⑤ 「굴착기의 작업능력 산정 비교」 표가 `E0 | 토사 | 0.60(불량)` 의 비고에 **「임 도」** 라고 못박는다 — 임도는 0.60 이 맞다(2026-09-18 원문 L5927·L5938 대조)."
},
{
"work_item_code": "FP-10-12-02",
"label": "암절취",
"unit_weight_ton_per_m3": "2.4",
"loose_factor": "1.35",
"bucket_factor": "0.55",
"loader_efficiency": "0.35",
"truck_efficiency": "0.9",
"loading_efficiency": "0.35",
"notes": [
"n 의 Qt = 계산값 T/γt×L(원문 표기 「10/(0.7×K)」의 10 은 토사 어림수 복사로 봄 — 버림) · 원문 10 이면 n 1.18배",
"Es = 운반 줄 값(적재 식의 E0 와 다른 기호)"
],
"loading_note": "E0 = 0.35(본문·표 「파쇄암」 같음)"
},
{
"work_item_code": "FP-10-12-03",
"label": "발파암",
"unit_weight_ton_per_m3": "2.4",
"loose_factor": "1.625",
"bucket_factor": "0.55",
"loader_efficiency": "0.35",
"truck_efficiency": "0.9",
"loading_efficiency": "0.35",
"notes": [
"n 의 Qt = 계산값 T/γt×L(원문 표기 「10/(0.7×K)」의 10 은 토사 어림수 복사로 봄 — 버림)",
"Es = 운반 줄 값(적재 식의 E0 와 다른 기호)",
"E 원문 누락 — 0.9 적용(같은 절 토사·암절취 · 덤프 작업효율)"
],
"loading_note": "E0 = 0.35(10-12-3 [주]③ 「파쇄암 0.35」)"
}
]
}
}
}
File diff suppressed because it is too large Load Diff