"""구조물도 그림 — 집수정 · 날개벽 · 물넘이포장 (관측 원단위, 2026-09-14 · 표 = 그림). ⚠ 집수정 □형 Ø800 — 그림 치수(`section`)로 **표 수량이 원문 식 그대로** 다시 나와야 함. 원문 값으로 바꿈: 콘크리트 2.84 → 2.83752 · 합판거푸집 21.28 → 21.2843 · 철근 4.78 → 4.776 · 면목 12.67 → 12.6728. """ from __future__ import annotations import math import sys from pathlib import Path ROOT = Path(__file__).resolve().parents[2] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) from B08_Quantity.B08_Quantity_Engine_ObservedUnit import load_observed_table # noqa: E402 from B08_Quantity.B08_Quantity_Engine_StructureFigure import attach_figures # noqa: E402 from B08_Quantity.B08_Quantity_Engine_StructureSheet import build_standard_sheets # noqa: E402 from B08_Quantity.B08_Quantity_Engine_UnitQuantity import build_table # noqa: E402 BASIN_SPEC = { "inlet_basin_form": "□형(기본형)", "inlet_basin_material": "콘크리트", "pipe_diameter_mm": "800", } def _sheets(**options) -> list[dict]: pipe = { "structure_id": "p1", "type_id": "pipe", "start_m": 0.0, "end_m": 0.0, "options": {"pipe_kind": "흄관", **options}, } payload = build_standard_sheets(build_table([pipe], None)) attach_figures(payload) return payload["sheets"] def _sheet(sheets: list[dict], type_id: str) -> dict: return next(sheet for sheet in sheets if sheet["type_id"] == type_id) def test_집수정_치수로_표_수량이_원문_식대로_다시_나온다() -> None: entry = load_observed_table().find("pipe_inlet_basin", BASIN_SPEC) s = entry["section"] amounts = {item["name"]: item["amount"] for item in entry["components"]} inner, wall, base = s["inner"], s["wall_m"], s["base_m"] ol, ow, h = inner["length_m"] + 2 * wall, inner["width_m"] + 2 * wall, inner["height_m"] il, iw = inner["length_m"], inner["width_m"] sp = s["spillway"] notch = (sp["top_m"] + sp["bottom_m"]) / 2 * sp["depth_m"] hole = 3.14 * (s["pipe_d_m"] / 2) ** 2 # 원문이 3.14 로 셈 concrete = ol * ow * h - il * iw * h - sp["count"] * notch * wall - hole * wall + ol * ow * base slant = math.hypot((sp["top_m"] - sp["bottom_m"]) / 2, sp["depth_m"]) form = ( (ol * h - hole) + (ol * h - notch) + (ow * h - notch) * 2 + (il * h - hole) + (il * h - notch) + (iw * h - notch) * 2 + (ol + ow) * base * 2 + slant * wall * sp["count"] * 2 ) chamfer = ol + il * 2 + slant * sp["count"] + sp["bottom_m"] * sp["count"] + wall * 4 trench = (ol + s["dig"]["extra_length_m"]) * s["dig"]["width_m"] * (h + base) backfill = trench - il * iw * (h + base) assert abs(concrete - amounts["콘크리트"]) < 1e-9 # 2.83752 assert abs(form - amounts["합판거푸집"]) < 5e-5 # 21.28432 assert abs(chamfer - amounts["면목"]) < 5e-5 # 12.67279 assert abs(trench - amounts["터파기"]) < 1e-9 assert abs(backfill - amounts["되메우기"]) < 1e-9 assert abs(trench - backfill - amounts["잔토처리"]) < 1e-9 def test_집수정_그림이_서고_날개벽은_까닭() -> None: sheets = _sheets(wing_wall_type="A-TYPE", **BASIN_SPEC) basin = _sheet(sheets, "pipe_inlet_basin") texts = [s["text"] for s in basin["figure"] if s["kind"] == "text"] assert "안 3×1×1.2 · 벽 0.2 · 바닥기초 3.4×1.4×0.2 m" in texts assert "터파기 3.8 × 2 × 깊이 1.4 m" in texts wing = _sheet(sheets, "pipe_wing_wall") assert ( wing["rows"] and wing["figure"] is None and "구조도 원본 확보 대기" in wing["figure_reason"] ) def test_돌집수정은_치수_원문이_없어_까닭() -> None: basin = _sheet( _sheets(pipe_diameter_mm="800", inlet_basin_form="돌집수정 ㄷ형"), "pipe_inlet_basin" ) assert basin["rows"] and basin["figure"] is None and "집계표 관측값" in basin["figure_reason"] def test_물넘이포장_그림과_표에_원문_차이_사유가_같다() -> None: structure = { "structure_id": "f1", "type_id": "ford_pavement", "start_m": None, "end_m": None, "options": {"ford_width_m": 5, "length_m": 10, "thickness_cm": 20}, } payload = build_standard_sheets(build_table([structure], None)) attach_figures(payload) sheet = payload["sheets"][0] texts = [s["text"] for s in sheet["figure"] if s["kind"] == "text"] diff = [note for note in sheet["notes"] if "원문 둘이 다름" in note] assert diff and diff[0] in texts assert "와이어메쉬 — 밑에서 0.133 m (2/3T)" in texts