Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016PSXsvyDxjNmufteN5bqmt
166 lines
7.5 KiB
Python
166 lines
7.5 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""122 B08 좌측 패널 옵션 — 배관 1본 길이 · 연암 / 경암 비율 · 저장 길 · 화면 배선.
|
||
|
||
1본 길이 정본 = 프로젝트 층 M02 구조물집계표 변수 `파형강관_1본_m` · 비율 정본 =
|
||
`project_settings.json › quantity › rock_ratios_pct`(기본 90 : 10 은 파일에 안 씀).
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import json
|
||
import re
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
|
||
from B08_Quantity import B08_Quantity_Router_Options as options
|
||
from B08_Quantity import B08_Quantity_Router_Summary as summary
|
||
from B08_Quantity.B08_Quantity_Engine_Earthwork_Summary import (
|
||
excel_round,
|
||
haul_lists,
|
||
split_rock_columns,
|
||
summary_rows,
|
||
)
|
||
from common_util.common_util_project_settings import settings_path, soft_rock_pct
|
||
from M02_MasterTemplete import M02_Template_Layers as layers
|
||
|
||
ROOT = Path(__file__).resolve().parents[2]
|
||
|
||
|
||
@pytest.fixture
|
||
def root(tmp_path, monkeypatch) -> Path:
|
||
system = tmp_path / "master"
|
||
(system / "table").mkdir(parents=True)
|
||
(system / "table/구조물집계표.json").write_text(
|
||
json.dumps({"열": [], "변수": {"파형강관_1본_m": 8, "측점간격_m": 20}}), encoding="utf-8"
|
||
)
|
||
monkeypatch.setattr(layers, "SYSTEM_ROOT", system)
|
||
project = tmp_path / "project"
|
||
project.mkdir()
|
||
return project
|
||
|
||
|
||
def test_기본값은_8_m_와_90_10_파일에_안_씀(root):
|
||
assert options.read_options(root) == {
|
||
"one_pipe_length_m": 8,
|
||
"rock_ratios_pct": {"연암": 90.0, "경암": 10.0},
|
||
"material_surcharge_pct": {"sand": 12, "gravel": 5, "cement": 3}, # 124
|
||
}
|
||
assert not settings_path(root).exists()
|
||
assert not layers.template_path(layers.project_dir(root), "table", "구조물집계표").exists()
|
||
|
||
|
||
def test_1본_6_m_저장_뒤_커플링을_다시_셈(root):
|
||
assert summary.couplings(22, summary.one_pipe_length(root)) == 2 # 올림(22/8) − 1
|
||
out = options.save_options(root, options.OptionsSave(one_pipe_length_m=6))
|
||
assert out["one_pipe_length_m"] == 6 == summary.one_pipe_length(root)
|
||
assert summary.couplings(22, 6) == 3 and summary.couplings(16, 6) == 2
|
||
# 프로젝트 층 사본 = 시스템 문서 그대로 + 변수 한 칸 · 시스템 층은 안 바뀜
|
||
mine = layers.read_template(layers.project_dir(root), "table", "구조물집계표")["문서"]
|
||
assert mine["변수"] == {"파형강관_1본_m": 6, "측점간격_m": 20} and mine["열"] == []
|
||
system = layers.read_template(layers.system_dir(), "table", "구조물집계표")["문서"]
|
||
assert system["변수"]["파형강관_1본_m"] == 8
|
||
# 다시 저장 = 같은 사본 판 위에 씀
|
||
options.save_options(root, options.OptionsSave(one_pipe_length_m=8))
|
||
assert summary.one_pipe_length(root) == 8
|
||
|
||
|
||
def test_비율_70_30_저장_뒤_남고_다른_칸은_그대로(root):
|
||
settings_path(root).write_text(
|
||
json.dumps(
|
||
{"quantity": {"rock_ratios_pct": {"풍화암": 5}, "rock_methods": {"연암": "ripping"}}}
|
||
),
|
||
encoding="utf-8",
|
||
)
|
||
out = options.save_options(root, options.OptionsSave(soft_rock_pct=70))
|
||
assert out["rock_ratios_pct"] == {"연암": 70.0, "경암": 30.0}
|
||
stored = json.loads(settings_path(root).read_text(encoding="utf-8"))["quantity"]
|
||
assert stored["rock_ratios_pct"] == {"풍화암": 5, "연암": 70.0, "경암": 30.0}
|
||
assert stored["rock_methods"] == {"연암": "ripping"}
|
||
assert options.read_options(root)["rock_ratios_pct"]["연암"] == 70.0
|
||
# 1본 길이는 안 보냈으니 그대로
|
||
assert options.read_options(root)["one_pipe_length_m"] == 8
|
||
|
||
|
||
def test_비율_읽기_자름_경암만_있을_때():
|
||
assert soft_rock_pct({}) == 90
|
||
assert soft_rock_pct({"rock_ratios_pct": {"연암": 130}}) == 100
|
||
assert soft_rock_pct({"rock_ratios_pct": {"경암": 25}}) == 75
|
||
assert soft_rock_pct({"rock_ratios_pct": {"연암": True}}) == 90
|
||
|
||
|
||
def test_범위_밖_값은_거절():
|
||
with pytest.raises(ValueError):
|
||
options.OptionsSave(one_pipe_length_m=0)
|
||
with pytest.raises(ValueError):
|
||
options.OptionsSave(soft_rock_pct=101)
|
||
|
||
|
||
def test_토공집계표_연암_경암은_암_곱하기_비율_합_100():
|
||
totals = {
|
||
"cut_soil_volume_m3": 10.0,
|
||
"cut_rock_volume_m3": 47.6,
|
||
"ditch_soil_volume_m3": 1.0,
|
||
"ditch_rock_volume_m3": 2.3,
|
||
"adjusted_total_m3": 60.0,
|
||
"fill_volume_m3": 5.0,
|
||
"diverted_m3": 5.0,
|
||
"balance_m3": 50.0,
|
||
"benching_area_m2": 0.0,
|
||
"grading_fill_area_m2": 0.0,
|
||
"grading_cut_area_m2": 0.0,
|
||
"tree_removal_fill_area_m2": 0.0,
|
||
"tree_removal_cut_area_m2": 0.0,
|
||
}
|
||
rows = summary_rows(totals, haul_lists(None), 70)
|
||
value = {(r["group"], r["kind"]): r["value"] for r in rows}
|
||
assert value[("흙깍기", "연암")] == excel_round(47.6 * 0.7) == 33
|
||
assert value[("흙깍기", "경암")] == excel_round(47.6 - 47.6 * 0.7) == 14
|
||
assert value[("측구 터파기", "연암")] == 2 and value[("측구 터파기", "경암")] == 1
|
||
# 토적표 칸 · 합계 — 연암 + 경암 = 암
|
||
record = split_rock_columns({"cut_rock_area_m2": 1.2, "cut_rock_volume_m3": 47.6}, 70)
|
||
assert record["cut_soft_rock_area_m2"] == pytest.approx(0.84)
|
||
assert record["cut_soft_rock_volume_m3"] + record["cut_hard_rock_volume_m3"] == pytest.approx(
|
||
47.6
|
||
)
|
||
|
||
|
||
def test_라우터_등록_응답_칸():
|
||
main = (ROOT / "main.py").read_text(encoding="utf-8")
|
||
assert "app.include_router(b08_options_router, dependencies=protected_with_company)" in main
|
||
earthwork = (ROOT / "B08_Quantity/B08_Quantity_Router_Earthwork.py").read_text(encoding="utf-8")
|
||
assert "split_rock_columns(record, soft)" in earthwork and '"rock_ratios_pct"' in earthwork
|
||
pipe = (ROOT / "B08_Quantity/B08_Quantity_Router_Summary.py").read_text(encoding="utf-8")
|
||
assert '"pipes": pipes' in pipe
|
||
|
||
|
||
def test_좌측_패널_컨테이너_셋과_저장_줄():
|
||
page = (ROOT / "B08_Quantity/B08_Quantity_UI_Page.ts").read_text(encoding="utf-8")
|
||
panel = (ROOT / "B08_Quantity/B08_Quantity_UI_Options.ts").read_text(encoding="utf-8")
|
||
assert "leftPanel: createOptionsPanel(projectId, () => select(current))" in page
|
||
for tab in ("structure", "earthwork", "hydrology"):
|
||
assert f'card("{tab}", "B08_Quantity_Tab_' in panel, tab
|
||
assert "ui-collapsible ui-sidebar-section ui-sidebar-card" in panel
|
||
assert (
|
||
'actions.className = "ui-sidebar-actions"' in panel and "attachCollapsible(panel)" in panel
|
||
)
|
||
assert len(re.findall(r'type: "number",\s+compact: true', panel)) == 4 # 할증 셋은 한 번(124)
|
||
# 조작 = 캐시 · [저장] = PUT 하나 · 자동 저장 없음
|
||
assert 'writeState("b08-options"' in panel and 'clearState("b08-options"' in panel
|
||
assert panel.count('method: "PUT"') == 1
|
||
registry = (ROOT / "A00_Common/b_page_state.ts").read_text(encoding="utf-8")
|
||
assert '"b08-options": { bucket: "draft", scope: "project" }' in registry
|
||
|
||
|
||
def test_표가_옵션_바뀜에_바로_다시_셈():
|
||
ui = (ROOT / "B08_Quantity/B08_Quantity_UI_Summary.ts").read_text(encoding="utf-8")
|
||
assert "recountCouplings(saved, liveOnePipe(projectId, saved.one_length_m))" in ui
|
||
assert "onOptionsChange(host," in ui
|
||
earth = (ROOT / "B08_Quantity/B08_Quantity_UI_Earthwork.ts").read_text(encoding="utf-8")
|
||
assert "splitRockSummary(summary, table.totals, soft)" in earth
|
||
assert "onOptionsChange(host," in earth
|
||
assert (
|
||
'[["절토", "연암"], "cut_soft_rock"' in earth
|
||
and '[["절토", "경암"], "cut_hard_rock"' in earth
|
||
)
|