# -*- 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 )