feat(b08): 안 고른 돌쌓기 뒷길이를 품셈 13-4-4 [주]⑩ 뒷길이 표준 하한으로 — 범위 안 첫 규격(메 ~3m 36→45) · 전개·양식·구조물도 한 벌 · 사유에 원문 줄 번호(B1)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
This commit is contained in:
2026-09-14 12:28:36 +09:00
co-authored by Claude Opus 5
parent f9ccc8f6fb
commit 7e036ddd51
13 changed files with 241 additions and 26 deletions
@@ -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)이 아님"
]
}
}
+7 -1
View File
@@ -19,7 +19,13 @@
"source": "judged_face_slope",
"note": "사용자 값이 있으면 그 값, 없으면 품셈 13-4-4 [주]⑪ 표준경사를 전개가 판정해 넘김"
},
"L3": { "label": "뒷길이(㎝)", "option": "back_len_cm", "default": 45 },
"L3": {
"label": "뒷길이(㎝)",
"option": "back_len_cm",
"default": 45,
"default_from": "standard_back_length",
"note": "안 고르면 품셈 13-4-4 [주]⑩ 뒷길이 표준 하한(높이·메/찰 · 범위 안 가장 작은 규격) — 전개와 한 벌(2026-09-14 브레인 판정 ㉮). 45 는 표를 못 읽을 때만"
},
"돌종류": { "label": "돌 종류", "option": "stone_kind", "default": "" },
"계수기준": { "label": "야면석 계수", "option": "stone_coeff_basis", "default": "" },
"TOP_IN": {
@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
"""뒷길이 표준(품셈 13-4-4 [주]⑩) — 안 고른 돌쌓기의 뒷길이는 **표의 하한** (2026-09-14 브레인 판정 ㉮).
앞서 붙박이 45㎝ 로 섰음. 표는 높이 × 메/찰 로 범위를 줌 — 하한이 규격(25·30·35·45·55·60·75)에
없으면 **범위 안 그 위 첫 규격**(메쌓기 ~3m·~5m 하한 36 → 45). 경계값은 앞 칸(표준경사와 같음).
"""
from __future__ import annotations
import sys
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from B08_Quantity.B08_Quantity_Engine_UnitQuantity_StoneSpec import ( # noqa: E402
_back_length,
back_length_default_note,
standard_back_length,
)
# (높이, 메쌓기 제안, 찰쌓기 제안) — 원문 L7174~7177
CELLS = [
(1.0, 25, 25),
(1.5, 25, 25), # 경계값은 앞 칸
(2.5, 45, 30), # 메 36~45 → 45 · 찰 30~35 → 30
(3.0, 45, 30),
(4.0, 45, 35), # 메 36~60 → 45 · 찰 35~45 → 35
(6.0, 45, 35), # 메 45~75 · 찰 35~55
(7.5, 75, 45), # 메 75이상 · 찰 45~60
]
@pytest.mark.parametrize("height, dry, wet", CELLS)
def test_표_하한(height: float, dry: int, wet: int) -> None:
assert standard_back_length(wet=False, height_m=height)[0] == dry
assert standard_back_length(wet=True, height_m=height)[0] == wet
def test_하한이_규격에_없으면_그_위_첫_규격_사유() -> None:
cm, basis = standard_back_length(wet=False, height_m=2.5)
assert cm == 45
assert "36~45㎝" in basis and "하한 36㎝ 는 규격 일곱(원문 L7152)에 없어" in basis
assert "원문 L7172~7181" in basis # 사용자가 되짚을 줄 번호(브레인 2026-09-14)
def test_고른_값이_이긴다() -> None:
assert _back_length({"back_len_cm": "55"}, wet=True, height_m=2.5) == 55
assert back_length_default_note({"back_len_cm": 55}, 55, wet=True, height_m=2.5) is None
def test_메찰_높이를_모르는_자리는_종전_45() -> None:
assert _back_length({}) == 45
assert "기본 45㎝" in (back_length_default_note({}, 45) or "")
@@ -38,6 +38,9 @@ def _structures() -> list[dict]:
"length_m": 10,
"foundation": "기초유",
"stone_kind": "야면석·호박돌",
# 2026-09-14 — 안 고르면 뒷길이 표준 하한(찰 ≤3m 30㎝)이라 야면석 관측 무게(35·45·55)가
# 없어 돌 줄이 안 섬. 돌 줄을 보는 시험이라 45 로 고름 · 빈 칸 길은 아래 시험.
"back_len_cm": 45,
},
},
{
@@ -76,6 +79,21 @@ def test_양식_갈음은_값을_안_움직인다() -> None:
assert stone["source"] == "uljin_library"
def test_뒷길이_안_고르면_양식도_전개와_같은_표준_하한() -> None:
"""품셈 13-4-4 [주]⑩ 하한(찰 ≤3m 30㎝)을 전개와 양식이 한 벌로 씀(브레인 판정 ㉮)."""
structures = _structures()[:1]
structures[0]["options"].pop("back_len_cm")
structures[0]["options"].pop("stone_kind")
names = {"masonry_wet": "돌쌓기(찰)"}
before = build_table(structures, names, None, None, 0.2, use_templates=False)["structures"][0]
after = build_table(structures, names, None, None, 0.2)["structures"][0]
assert after["library_item"] == "돌쌓기(찰)"
assert [c["name"] for c in after["components"]] == [c["name"] for c in before["components"]]
for got, want in zip(after["components"], before["components"]):
assert got["amount"] == pytest.approx(want["amount"], rel=1e-9, abs=1e-12), got["name"]
assert any("하한 30㎝" in note for note in before["notes"])
def test_자재총괄_갈래와_배합_성분() -> None:
names = {"masonry_wet": "돌쌓기(찰)", "masonry_dry": "돌쌓기(메)"}
before = build_material_table(
@@ -89,7 +89,9 @@ def test_기본값으로_그린_장은_표_사유와_같은_말을_적는다() -
options = {"height_m": 2.0}
_rows, table_notes = stone_masonry(2.0, 1.0, options, wet=False)
table_default = [note for note in table_notes if "뒷길이를 안 골라" in note]
assert table_default == [back_length_default_note(options, 45)]
# 2026-09-14 — 기본은 뒷길이 표준 하한(메 ≤3m 36~45㎝ → 45) · 표·그림이 같은 말.
assert table_default == [back_length_default_note(options, 45, wet=False, height_m=2.0)]
assert "하한 36㎝ 는 규격 일곱" in table_default[0]
texts = _texts(build_figure(_sheet()))
assert table_default[0] in texts
@@ -61,7 +61,16 @@ CASES = [
dict(height=2.0, length=7.5, back_len_cm=45, face_slope_ratio=0.35, foundation="기초유"),
dict(height=2.0, length=10.0, back_len_cm=55, thickness_top_m=0.9, foundation="기초버림"),
dict(height=2.0, length=10.0, back_len_cm=45, thickness_bottom_m="1.4", stone_kind="깬잡석"),
dict(height=2.0, length=10.0, stone_kind="야면석·호박돌", stone_coeff_basis="실무 관행"),
# 뒷길이 45 — 안 고르면 표준 하한 30㎝ 라 야면석 관측 무게(35·45·55)가 없어 양식만 오류 줄(명세 13장 Ⓐ)
dict(
height=2.0,
length=10.0,
back_len_cm=45,
stone_kind="야면석·호박돌",
stone_coeff_basis="실무 관행",
),
# 뒷길이 안 고름 — 전개·양식이 같은 표준 하한(찰 ≤3m 30㎝, 브레인 판정 ㉮)
dict(height=2.0, length=10.0, stone_kind="깬돌", foundation="기초유"),
dict(height=1.5, length=10.0, back_len_cm=35, weep_hole_area_m2=3, foundation="기초유"),
dict(height=2.5, length=12.0, back_len_cm=60, stone_kind="견치돌", rubble=0.3),
# 줄이 안 서는 갈래 — `when`
+3 -2
View File
@@ -125,7 +125,8 @@ def test_실무_시트_m당_값_재현() -> None:
품셈 1-2-2 의 소수 자리는 표기 규칙이고 계산은 전정밀로 둔다(PLAN 8-16).
맞추려고 식을 비틀지 않는다 — 허용오차를 그 차이만큼 둔다.
"""
result = expand(돌쌓기찰(height=1.5, length=1.0))
# 실무 시트가 뒷길이 45 — 2026-09-14 부터 안 고르면 표준 하한(찰 ≤1.5m 25㎝)이라 고름.
result = expand(돌쌓기찰(height=1.5, length=1.0, back_len_cm=45))
assert result.height_m == 1.5
assert result.length_m == 1.0
assert 성분(result, "터파기").amount == pytest.approx(1.55, abs=0.02)
@@ -135,7 +136,7 @@ def test_실무_시트_m당_값_재현() -> None:
def test_평균두께는_전정밀로() -> None:
"""실무가 0.83 으로 반올림해 재계산한 자리 — 우리는 0.825 를 그대로 쓴다."""
result = expand(돌쌓기찰(height=1.5, length=1.0))
result = expand(돌쌓기찰(height=1.5, length=1.0, back_len_cm=45))
# 터파기 = 높이 × (평균두께 + 0.2) × 연장 = 1.5 × 1.025 = 1.5375
assert 성분(result, "터파기").amount == pytest.approx(1.5375)