Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1

This commit is contained in:
2026-09-13 17:25:27 +09:00
10 changed files with 227 additions and 31 deletions
+1 -2
View File
@@ -7,8 +7,7 @@
"note": "구조물도 양식형 항목 첫 벌(PLAN 3장 ② · 명세 13장 식 칸 계약). 값은 지금 전개(`B08_Quantity_Engine_UnitQuantity.stone_masonry` + 기초잡석)와 같게 둠(판정 Ⓑ). 규격(높이·뒷길이·돌종류)마다 항목을 늘리지 않고 제원 vars 와 표 한 벌로 수량이 다시 남(명세 16장).",
"differs_from_engine": [
"원문 「-」 칸(야면석 75·깬돌 25·견치돌 25/30 고임돌)에서 전개는 고임돌 줄을 안 세우고 채집석에 0 을 더함. 이 양식은 고임돌 줄이 「표 칸이 비어 있음」 오류로 서고 채집석도 막힘 — 명세 13장 Ⓐ(안 선 줄을 0 으로 치지 않음)을 따른 것",
"돌 줄 이름 — 전개는 돌종류 이름(야면석·호박돌…/돌), 양식은 이름 「돌」 고정 + spec 에 종류(명세 13장 Ⓒ)",
"「실무 관행」 계수를 고르면 전개는 돌종류까지 지워 돌 줄을 「돌」·계산식 무게로 세움(야면석을 골라도). 관행은 계수 열만 바꾸는 칸이라 전개 쪽 결함으로 보고 양식은 종류를 지킴 — 판정 요청"
"돌 줄 이름 — 전개는 돌종류 이름(야면석·호박돌…/돌), 양식은 이름 「돌」 고정 + spec 에 종류(명세 13장 Ⓒ)"
],
"vars": {
"H": { "label": "높이(m)", "source": "height_m" },
+23 -3
View File
@@ -65,12 +65,32 @@ def test_폭은_하단_길이에서_온다() -> None:
assert "잠정" not in got.basis
def test_기초잡석은_두께가_없어_안_() -> None:
"""⚠ 폭은 생겼지만 **두께가 원문에 없다** — 지어내지 않고 사유로 드러낸다."""
def test_기초잡석은_계상하고_안_선다는_사유를_안_남긴() -> None:
"""ⓘ 2026-09-13 바뀜 — 옛 시험은 「두께가 원문에 없어 안 선다」를 굳혔음.
확정 3차 ② 로 두께 0.2 가 서고 `build_table` 이 기초잡석 줄을 세우는데, 전개는 여전히
「안 섬」 사유를 붙여 **구조물도에 줄과 사유가 함께 떠 서로 어긋났음**(화면 실측).
브레인 판정(PLAN 10장 「기초잡석 계상함」)대로 사유를 걷고 줄이 서는지 봄.
"""
from B08_Quantity.B08_Quantity_Engine_UnitQuantity import build_table
_, notes = stone_masonry(
2.5, 10.0, {"height_m": 2.5, "length_m": 10.0, "back_len_cm": 45}, wet=True
)
assert any("기초잡석(12-25)" in n and "두께가 원문에 없음" in n for n in notes)
assert not any("기초잡석(12-25)" in n and "안 섬" in n for n in notes)
table = build_table(
[
{
"structure_id": "s1",
"type_id": "masonry_wet",
"start_m": 0.0,
"end_m": 10.0,
"options": {"height_m": 2.5, "length_m": 10.0, "back_len_cm": 45},
}
]
)
names = [c["name"] for c in table["structures"][0]["components"]]
assert "기초잡석" in names
def test_큰돌쌓기에도_선다() -> None:
@@ -82,6 +82,36 @@ def test_조회가_장을_낸다(client: TestClient) -> None:
assert all("spec" in row for row in sheet["rows"])
def test_양식이_있는_종류는_줄마다_식을_싣는다(client: TestClient) -> None:
"""PLAN 3장 ④ · 명세 13장 — 찰쌓기는 양식으로 줄이 서고 식·설명·갈 곳·출처가 실림."""
sheet = _sheet(client)
assert sheet["library_item"]["type_id"] == "masonry_wet"
rows = {row["name"]: row for row in sheet["rows"]}
돌쌓기 = rows["돌쌓기"]
assert 돌쌓기["formula"] == "H*L*SQRT(1+N^2)"
assert "비탈면적" in 돌쌓기["basis"]
assert 돌쌓기["source"] == "library" and 돌쌓기["destination"] == "unit_price"
assert all(row["destination"] for row in sheet["rows"]) # 갈 곳 빈 줄 없음
# 돌 줄은 이름 고정 — 종류를 안 골라 규격이 빔(매칭 성공 아님, 명세 13장 Ⓒ).
assert rows[""]["spec"] == ""
# m당 값 — 제원(H=2.5·뒷길이 45·1:0.3)으로 다시 셈한 값.
assert 돌쌓기["unit_amount"] == pytest.approx(2.5 * (1 + 0.3**2) ** 0.5)
def test_안_선_줄은_안_섬으로_실린다(client: TestClient) -> None:
"""버림을 「안 넣음」으로 저장하면 버림·기초잡석이 0 이 아니라 「안 섬」으로 옴."""
sheet = _sheet(client)
saved = client.put(
f"{SHEETS}/spec",
json={"sheet_key": sheet["key"], "base_revision": 1, "blinding_concrete": "안 넣음"},
)
assert saved.status_code == 200, saved.text
rows = {row["name"]: row for row in _sheet(client)["rows"]}
for name in ("버림콘크리트", "기초잡석"):
assert rows[name]["skipped"] is True and rows[name]["unit_amount"] is None, rows[name]
assert rows[name]["reason"], rows[name]
def test_기초잡석_두께가_산출_조건을_따른다(client: TestClient, project: Path) -> None:
기본 = _unit_amount(_sheet(client), "기초잡석")
(project / "project_settings.json").write_text(
@@ -99,14 +99,8 @@ def test_양식_값이_전개와_같다(case: dict) -> None:
[r["name"] for r in rows],
[c["name"] for c in components],
)
# ⚠ 알려진 차이 하나 — 「실무 관행」 계수를 고르면 전개가 돌종류까지 지워 돌 줄이 이름 「돌」·
# 계산식 무게로 섬(야면석을 골라도). 관행은 **계수 열만** 바꾸는 칸이라 전개 쪽 결함으로 보고
# 양식은 종류를 지킴 — 양식 `differs_from_engine` · PLAN 3장에 판정 요청으로 적음.
practice_drops_kind = case.get("stone_coeff_basis") == "실무 관행" and case.get("stone_kind")
# ⓘ 「실무 관행」 계수가 돌종류를 지우던 전개 결함은 2026-09-13 고침(브레인 판정) — 차이 없음.
for row, component in zip(rows, components):
if row["name"] == "" and practice_drops_kind:
assert (component["name"], row["spec"]) == ("", case["stone_kind"])
continue
if row["name"] == "":
# 명세 13장 Ⓒ — 양식은 이름 고정 + 규격에 종류, 전개는 종류를 이름으로 씀.
assert component["name"] == (row["spec"] or "")
@@ -271,6 +271,28 @@ def test_물구멍_잠정값이_근거에_적힐것() -> None:
assert "2~3㎡" in basis # 법이 정한 범위
def test_실무_관행_계수가_돌종류를_지우지_않는다() -> None:
"""2026-09-13 브레인 판정 — 계수 열 고르기와 돌종류는 다른 축.
옛 전개는 「실무 관행」을 고르면 야면석이 「돌」·계산식 무게(0.918)로 조용히 섰음.
이제 계수만 참고자료 열(고임돌 0.15)로 가고, 돌은 야면석 이름·관측 무게(0.88)를 지킴.
"""
result = expand(
돌쌓기찰(
height=2.0,
length=10.0,
back_len_cm=45,
stone_kind="야면석·호박돌",
stone_coeff_basis="실무 관행",
face_slope_ratio=0.3,
)
)
돌쌓기 = 성분(result, "돌쌓기").amount
assert 성분(result, "야면석·호박돌").amount == pytest.approx(돌쌓기 * 0.88)
assert 성분(result, "고임돌").amount == pytest.approx(돌쌓기 * 0.15) # 관행 열
assert not any(c.name == "" for c in result.components)
def test_물구멍관_길이는_평균두께() -> None:
"""2026-09-13 브레인 판정(PLAN 10장) — 개소당 관 길이 = 평균두께(옛 상수 0.5m 아님).