tmp/ 가 창끼리 안 건너가는 것이 확정돼(랩탑이 시간 두고 두 번 확인) 시험·예외가 저절로 건너가도록 git 안으로 옮김. 사용자 확정. - 내용은 하나도 안 고침 — 자리만 옮김. tmp/tests 는 남겨 둠. - 같은 이름이 이미 있던 64 개는 랩탑 것을 그대로 두고 건너뜀. - helper_b05_*.js 둘은 랩탑이 .cjs 로 이미 올린 것과 **줄바꿈만 다른 같은 내용**이라 복사본을 도로 뺌(시험이 .cjs 를 부름). - resources/tester/ 에서 전체 1176 통과 · 29 건너뜀 · 실패 0. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
210 lines
7.3 KiB
Python
210 lines
7.3 KiB
Python
"""유토곡선에 넘길 구조물 몫 — 채집석 공제 · 구조물 잔토 (2026-09-08).
|
|
|
|
⚠ 겨누는 것 다섯
|
|
① 둘 다 **양수**로 낸다 — 빼고 더하는 것은 받는 쪽(B06) 몫
|
|
② ⚠ 「아직 안 옴(None)」과 「없음(0)」을 가른다
|
|
③ **측점별로도** 낸다 — 총량만 주면 잔량 비례로 흩어져 운반거리가 틀어진다
|
|
④ 구조물이 구간이라 **가운데 측점**을 자리로 본다
|
|
⑤ 칸 이름이 받는 쪽과 같은 낱말이다
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[2]
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
from B08_Quantity.B08_Quantity_Engine_HaulInputs import ( # noqa: E402
|
|
STRUCTURE_SPOIL_KEY,
|
|
STRUCTURE_SPOIL_POINTS_KEY,
|
|
haul_inputs,
|
|
)
|
|
from B08_Quantity.B08_Quantity_Engine_UnitQuantity import ( # noqa: E402
|
|
COLLECTED_STONE_KEY,
|
|
build_table,
|
|
)
|
|
|
|
|
|
def 표(*ranges: tuple[float, float]) -> dict:
|
|
return build_table(
|
|
[
|
|
{
|
|
"structure_id": f"s{index}",
|
|
"type_id": "masonry_wet",
|
|
"start_m": start,
|
|
"end_m": end,
|
|
"options": {
|
|
"height_m": 2.5,
|
|
"length_m": end - start,
|
|
"back_len_cm": 45,
|
|
"foundation": "기초유",
|
|
},
|
|
}
|
|
for index, (start, end) in enumerate(ranges)
|
|
]
|
|
)
|
|
|
|
|
|
def test_둘_다_양수로_낸다() -> None:
|
|
got = haul_inputs(표((0.0, 10.0)))
|
|
assert got[COLLECTED_STONE_KEY] > 0
|
|
assert got[STRUCTURE_SPOIL_KEY] > 0
|
|
|
|
|
|
def test_아직_안_옴과_없음을_가른다() -> None:
|
|
"""⚠ 0 으로 눅이면 받는 쪽이 「없다」와 「못 받았다」를 못 가른다."""
|
|
빈값 = haul_inputs(None)
|
|
assert 빈값[STRUCTURE_SPOIL_KEY] is None
|
|
assert 빈값[COLLECTED_STONE_KEY] is None
|
|
assert 빈값[STRUCTURE_SPOIL_POINTS_KEY] == []
|
|
|
|
|
|
def test_측점별로도_낸다() -> None:
|
|
got = haul_inputs(표((0.0, 10.0), (100.0, 110.0)))
|
|
points = got[STRUCTURE_SPOIL_POINTS_KEY]
|
|
assert [row["chainage_m"] for row in points] == [5.0, 105.0]
|
|
assert abs(sum(row["spoil_m3"] for row in points) - got[STRUCTURE_SPOIL_KEY]) < 0.01
|
|
|
|
|
|
def test_측점_차례로_선다() -> None:
|
|
"""⚠ 받는 쪽이 잔량에 얹을 때 차례가 어긋나면 운반거리가 틀어진다."""
|
|
points = haul_inputs(표((100.0, 110.0), (0.0, 10.0)))[STRUCTURE_SPOIL_POINTS_KEY]
|
|
assert [row["chainage_m"] for row in points] == [5.0, 105.0]
|
|
|
|
|
|
def test_칸_이름이_받는_쪽과_같다() -> None:
|
|
assert STRUCTURE_SPOIL_KEY == "structure_spoil_m3"
|
|
assert COLLECTED_STONE_KEY == "collected_stone_deduction_m3"
|
|
|
|
|
|
def test_지반_갈래를_함께_낸다() -> None:
|
|
"""⚠ 새 판정이 아니라 **구조물터파기가 쓰는 그 값**이다 — 두 벌로 짜면 갈린다."""
|
|
unit = 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,
|
|
"foundation": "기초유",
|
|
},
|
|
}
|
|
],
|
|
None,
|
|
None,
|
|
{0.0: "ripping_rock", 10.0: "ripping_rock"},
|
|
)
|
|
point = haul_inputs(unit)[STRUCTURE_SPOIL_POINTS_KEY][0]
|
|
assert point["ground_type"] == "ripping_rock"
|
|
assert point["ground_label"] == "암절취"
|
|
assert "암절취" in point["ground_basis"]
|
|
|
|
|
|
def test_섞인_구조물은_모름으로_보낸다() -> None:
|
|
"""⚠ 터파기에서 다수결로 안 고른 그 규칙 그대로 — 받는 쪽이 「모르는 몫」으로 드러낸다."""
|
|
unit = 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,
|
|
"foundation": "기초유",
|
|
},
|
|
}
|
|
],
|
|
None,
|
|
None,
|
|
{0.0: "soil", 10.0: "blasting_rock"},
|
|
)
|
|
point = haul_inputs(unit)[STRUCTURE_SPOIL_POINTS_KEY][0]
|
|
assert point["ground_type"] is None
|
|
assert "섞여" in point["ground_basis"]
|
|
|
|
|
|
def test_상태를_값으로_적어_보낸다() -> None:
|
|
"""⚠⚠ 유토곡선 잔량은 **다짐상태**인데 이 값은 **자연상태**다 — 그냥 더하면 섞인다.
|
|
|
|
말로만 두면 다음 사람이 못 본다. 총량에도, 측점마다도 상태를 적는다.
|
|
"""
|
|
from B08_Quantity.B08_Quantity_Engine_HaulInputs import (
|
|
VOLUME_BASIS_KEY,
|
|
VOLUME_BASIS_NATURAL,
|
|
)
|
|
|
|
got = haul_inputs(표((0.0, 10.0)))
|
|
assert got[VOLUME_BASIS_KEY] == VOLUME_BASIS_NATURAL == "natural"
|
|
assert all(row[VOLUME_BASIS_KEY] == "natural" for row in got[STRUCTURE_SPOIL_POINTS_KEY])
|
|
# 빈 값일 때도 칸은 있다 — 받는 쪽이 「상태를 모른다」와 「값이 없다」를 안 헷갈리게.
|
|
assert haul_inputs(None)[VOLUME_BASIS_KEY] == "natural"
|
|
|
|
|
|
def test_채집석을_갈래별로_낸다() -> None:
|
|
"""⚠ 축을 맞추려면 갈래가 있어야 한다 — 받는 쪽이 ×C 로 다짐 축에 맞춰 뺀다."""
|
|
from B08_Quantity.B08_Quantity_Engine_HaulInputs import (
|
|
COLLECTED_STONE_BY_GROUND_KEY,
|
|
COLLECTED_STONE_UNKNOWN_KEY,
|
|
)
|
|
|
|
unit = 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},
|
|
}
|
|
],
|
|
None,
|
|
None,
|
|
{0.0: "ripping_rock", 10.0: "ripping_rock"},
|
|
)
|
|
got = haul_inputs(unit)
|
|
assert got[COLLECTED_STONE_BY_GROUND_KEY]["ripping_rock"] > 0
|
|
assert got[COLLECTED_STONE_UNKNOWN_KEY] == 0.0
|
|
# 갈래별 합이 총량과 같다 — 어느 쪽으로도 새지 않는다.
|
|
assert abs(sum(got[COLLECTED_STONE_BY_GROUND_KEY].values()) - got[COLLECTED_STONE_KEY]) < 0.01
|
|
|
|
|
|
def test_갈래를_못_가른_몫은_따로_낸다() -> None:
|
|
"""⚠ 계수가 없으므로 환산하지 않는다 — 받는 쪽이 그 사실을 알아야 한다."""
|
|
from B08_Quantity.B08_Quantity_Engine_HaulInputs import (
|
|
COLLECTED_STONE_BY_GROUND_KEY,
|
|
COLLECTED_STONE_UNKNOWN_KEY,
|
|
)
|
|
|
|
unit = 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},
|
|
}
|
|
],
|
|
None,
|
|
None,
|
|
{0.0: "soil", 10.0: "blasting_rock"},
|
|
)
|
|
got = haul_inputs(unit)
|
|
assert got[COLLECTED_STONE_BY_GROUND_KEY] == {}
|
|
assert got[COLLECTED_STONE_UNKNOWN_KEY] > 0
|
|
|
|
|
|
def test_원문이_없다는_사실을_값과_함께_보낸다() -> None:
|
|
"""⚠ 벽 입적 ↔ 원바닥 암 관계를 정한 원문이 없다 — 받는 쪽이 그 한계를 알아야 한다."""
|
|
basis = haul_inputs(표((0.0, 10.0)))["collected_stone_basis"]
|
|
assert "원문이 없음" in basis and "실무 시트는 그냥 뺌" in basis
|