From 8f4ec487599e8bba19e0c14ddace3e9ff66a6e81 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Mon, 14 Sep 2026 21:11:44 +0900 Subject: [PATCH] =?UTF-8?q?feat(b06=C2=B7b08):=20=E3=89=B1=20(=EB=82=98)?= =?UTF-8?q?=20=EC=95=94=20=ED=99=98=EC=82=B0=EA=B3=84=EC=88=98=EB=A5=BC=20?= =?UTF-8?q?=EA=B5=AC=EC=84=B1=EB=B9=84=20=EA=B0=80=EC=A4=91=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=E2=80=94=20=ED=86=A0=EC=A0=81=ED=91=9C=C2=B7?= =?UTF-8?q?=EC=9C=A0=ED=86=A0=EA=B3=A1=EC=84=A0=C2=B7=EC=9A=B4=EB=B0=98?= =?UTF-8?q?=ED=91=9C=EA=B0=80=20=ED=95=9C=20=ED=95=A8=EC=88=98(mixed=5Fcon?= =?UTF-8?q?version=5Ffactors)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - B06 암은 한 종류 자리표시라 토적표 보정량·유토곡선이 리핑암 C 하나로 쌓았음 · 구성비·갈래별 시공법이 다 서면 암 두 칸을 Σ몫×C(시공법)로 · 비면 종전 값(인계가 막힘 사유) - 계수를 내는 곳 넷(B06 서버 재계산·곡선 문맥 · B08 토적표·사토 계수)이 같은 함수 · 화면 「무엇을 골랐나」는 갈래별 값 그대로 - 936be972 연암 60 리핑/보통암 40 발파 · 서버 재계산: 토적표 깎기 보정량 6,296.81 = 유토곡선 6,296.8 · 암 보정량 4,167.31 → 4,384.74 · 토취 7,259.84 → 7,053.83 · 사토 55.31 → 58.2 · 운반 검산 0 · 되돌려 재계산 원래 값 그대로 Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq --- .../B06_Section_Server_Calc_Prebuild.py | 7 +- B08_Quantity/B08_Quantity_Router_Earthwork.py | 7 +- common_util/common_util_project_settings.py | 30 +++++++++ .../tester/test_rock_mixed_conversion.py | 66 +++++++++++++++++++ 4 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 resources/tester/test_rock_mixed_conversion.py diff --git a/B06_Section/B06_Section_Server_Calc_Prebuild.py b/B06_Section/B06_Section_Server_Calc_Prebuild.py index d032de2d..be311607 100644 --- a/B06_Section/B06_Section_Server_Calc_Prebuild.py +++ b/B06_Section/B06_Section_Server_Calc_Prebuild.py @@ -39,8 +39,8 @@ from B06_Section.B06_Section_Repository import ( from B06_Section.B06_Section_Repository_Bulk import merge_cross_section_designs from common_util.common_util_node_bundle import run_bundle_json from common_util.common_util_project_settings import ( - earthwork_conversion_factors, haul_equipment_limits, + mixed_conversion_factors, quantity_settings, ) from common_util.common_util_storage import resolve_stored_project_path @@ -96,7 +96,8 @@ async def conversion_factors_for(project_id: Any) -> dict[str, dict[str, float]] except Exception: logger.warning("B06 프로젝트 경로를 못 찾음 — 기본 계수로 진행: project_id=%s", project_id) return {kind: dict(entry) for kind, entry in EARTHWORK_CONVERSION_FACTORS.items()} - return earthwork_conversion_factors(quantity_settings(root)) + # 암은 구성비 가중 C(㉱ (나)) — 토적표·운반표와 같은 함수. + return mixed_conversion_factors(quantity_settings(root)) async def haul_limits_for(project_id: Any) -> list[tuple[str, float | None]]: @@ -235,7 +236,7 @@ async def _recompute(project_id: UUID | str, route_id: int) -> int: "detail": detail, "context": _mass_haul_context( haul_inputs, - earthwork_conversion_factors(quantity_settings(project_root)), + mixed_conversion_factors(quantity_settings(project_root)), haul_equipment_limits(quantity_settings(project_root)), ), }, diff --git a/B08_Quantity/B08_Quantity_Router_Earthwork.py b/B08_Quantity/B08_Quantity_Router_Earthwork.py index 26640669..842db03c 100644 --- a/B08_Quantity/B08_Quantity_Router_Earthwork.py +++ b/B08_Quantity/B08_Quantity_Router_Earthwork.py @@ -55,7 +55,7 @@ from common_util.common_util_project_settings import ( application_ratio, concrete_placing_method, earthwork_conversion_choices, - earthwork_conversion_factors, + mixed_conversion_factors, haul_limit_choice, quantity_settings, rock_classes, @@ -98,7 +98,8 @@ async def get_earthwork_table(project_id: UUID, route_id: int) -> JSONResponse: ) # ⚠ 설정을 **먼저** 읽는다 — 토량환산계수를 프로젝트가 골랐으면 표가 그 값으로 서야 한다. settings, project_root = await _project_settings(project_id) - factors = earthwork_conversion_factors(settings) + # 암은 구성비 가중 C(㉱ (나)) — 유토곡선(B06)·운반표와 같은 함수. + factors = mixed_conversion_factors(settings) table = build_table(_stations(designs), factors) # 화면이 「무엇을 골랐나 · 품셈 범위 안인가」를 보이는 데 쓴다. 계산에는 안 들어간다. table["conversion_factor_choices"] = earthwork_conversion_choices(settings) @@ -246,7 +247,7 @@ _GROUND_KIND_OF = {"ea_m3": "soil", "rr_m3": "ripping_rock", "br_m3": "blasting_ def _compacted_factor(settings: dict[str, Any]) -> dict[str, float]: """갈래 칸 ↔ 다짐 환산계수 `C` — 프로젝트가 고른 값이 있으면 그것이 선다.""" - factors = earthwork_conversion_factors(settings) + factors = mixed_conversion_factors(settings) return {key: float(factors[kind]["compacted"]) for key, kind in _GROUND_KIND_OF.items()} diff --git a/common_util/common_util_project_settings.py b/common_util/common_util_project_settings.py index 80e8ea6d..68c8fcc2 100644 --- a/common_util/common_util_project_settings.py +++ b/common_util/common_util_project_settings.py @@ -323,6 +323,36 @@ def earthwork_conversion_factors(settings: dict[str, Any]) -> dict[str, dict[str return resolved +#: 암 시공법 → 환산계수 갈래 이름(`EARTHWORK_CONVERSION_FACTORS` 키). +ROCK_METHOD_KINDS = {ROCK_METHOD_RIPPING: "ripping_rock", ROCK_METHOD_BLASTING: "blasting_rock"} + + +def mixed_conversion_factors(settings: dict[str, Any]) -> dict[str, dict[str, float]]: + """토적표·유토곡선·운반표가 **계산에** 쓸 계수 — 암은 구성비 가중 C (㉱ (나) · 2026-09-14). + + B06 의 암은 한 종류 **자리표시**(`ripping_rock` · 옛 자료 `blasting_rock`)이고 암질은 설계내역에서 + 구성비로 정함(8-1 사용자 확정). 그래서 구성비와 갈래별 시공법이 **다 서면** 암 두 칸을 + Σ몫 × C(시공법)로 갈음 — 몫은 흙깎기와 같은 안분(준 비율끼리 · 합이 100 이 아니어도 안분). + ⚠ 구성비가 비거나 시공법이 빠진 갈래가 있으면 **종전 값** — 인계가 막힘 사유를 내고, 지어낸 계수를 안 씀. + ⚠ 화면 「무엇을 골랐나」(`earthwork_conversion_choices`)는 갈래별 값 그대로 — 여기 값을 보이면 안 됨. + """ + resolved = earthwork_conversion_factors(settings) + ratios = settings.get("rock_ratios_pct") or {} + shares = { + name: float(ratios.get(name) or 0) + for name in rock_classes(settings) + if name != "토사" and float(ratios.get(name) or 0) > 0 + } + kinds = {name: ROCK_METHOD_KINDS.get(rock_method(settings, name) or "") for name in shares} + if not shares or not all(kinds.values()): + return resolved + total = sum(shares.values()) + mixed = sum(shares[name] / total * resolved[kind]["compacted"] for name, kind in kinds.items()) + for kind in ROCK_METHOD_KINDS.values(): + resolved[kind]["compacted"] = mixed + return resolved + + def earthwork_conversion_choices(settings: dict[str, Any]) -> dict[str, dict[str, Any]]: """갈래별 「무엇을 골랐나」 — 화면이 기본값과 고른 값을 갈라 보이는 데 쓴다. diff --git a/resources/tester/test_rock_mixed_conversion.py b/resources/tester/test_rock_mixed_conversion.py new file mode 100644 index 00000000..639823a9 --- /dev/null +++ b/resources/tester/test_rock_mixed_conversion.py @@ -0,0 +1,66 @@ +"""㉱ (나) 암 환산계수를 구성비 가중으로 — 2026-09-14 브레인 판정(구성비가 정본 · (가) 뒤 차례). + +B06 의 암은 한 종류 자리표시(`ripping_rock`)라 토적표 보정량·유토곡선이 리핑암 C 하나로 쌓았음 — +구성비 60/40(리핑·발파)이면 토적표 암 보정량이 217.43㎥ 덜 섬(936be972 실측). ⇒ 계수를 내는 한 곳에서 +암 두 칸을 Σ몫×C(시공법)로 · 구성비나 시공법이 비면 종전 값(인계가 막힘 사유를 냄 · 지어낸 계수 안 씀). +""" + +from __future__ import annotations + +import sys +from pathlib import Path + +import pytest + +ROOT = Path(__file__).resolve().parents[1].parent +if str(ROOT) not in sys.path: + sys.path.insert(0, str(ROOT)) + +from common_util.common_util_project_settings import ( # noqa: E402 + earthwork_conversion_choices, + earthwork_conversion_factors, + mixed_conversion_factors, +) + +MIX = { + "rock_class_set": "geochang5", + "rock_ratios_pct": {"연암": 60, "보통암": 40}, + "rock_methods": {"연암": "ripping", "보통암": "blasting"}, +} + + +def test_구성비와_시공법이_서면_암_두_칸이_가중_C() -> None: + factors = mixed_conversion_factors(MIX) + for kind in ("ripping_rock", "blasting_rock"): + assert factors[kind]["compacted"] == pytest.approx(0.6 * 1.15 + 0.4 * 1.30) + assert factors["soil"] == earthwork_conversion_factors(MIX)["soil"] + + +def test_고른_계수_위에서_가중() -> None: + settings = {**MIX, "conversion_factors_override": {"ripping_rock": {"compacted": 1.0}}} + assert mixed_conversion_factors(settings)["ripping_rock"]["compacted"] == pytest.approx( + 0.6 * 1.0 + 0.4 * 1.30 + ) + + +@pytest.mark.parametrize( + "settings", + [ + {"rock_class_set": "geochang5"}, + {**MIX, "rock_methods": {"연암": "ripping"}}, + ], +) +def test_구성비나_시공법이_비면_종전_값(settings: dict) -> None: + assert mixed_conversion_factors(settings) == earthwork_conversion_factors(settings) + + +def test_화면_고른_값_표시는_갈래별_그대로() -> None: + assert earthwork_conversion_choices(MIX)["ripping_rock"]["compacted"] == pytest.approx(1.15) + + +def test_토적표_유토곡선_운반표가_같은_함수를_씀() -> None: + prebuild = (ROOT / "B06_Section" / "B06_Section_Server_Calc_Prebuild.py").read_text("utf-8") + router = (ROOT / "B08_Quantity" / "B08_Quantity_Router_Earthwork.py").read_text("utf-8") + assert prebuild.count("mixed_conversion_factors(") == 2 + assert "earthwork_conversion_factors(" not in prebuild + assert router.count("mixed_conversion_factors(settings)") == 2