# -*- coding: utf-8 -*- """표준도 제원 — 버림 콘크리트 칸(확정 8-2·14)과 물빼기 그림 주석(확정 5차 작은 것 2). **버림 콘크리트** — 셈은 이미 「기본은 넣고, 「안 넣음」이면 뺀다」로 서 있었는데 (`wants_blinding`) **끄는 칸이 화면에 없었다**. 등록부 · 표준도 입력 · 폼 세 자리를 함께 이어 놓은 것을 굳힌다. ⚠ **문구가 폴리시다** — 폼이 보내는 말이 `wants_blinding` 이 「빼기」로 읽는 말과 한 글자라도 다르면 골라도 안 빠진다. 그래서 선택지 문구를 **엔진에 직접 물어** 확인한다. **물빼기 주석** — 그림이 기본 상수를 적고 있어, 구조물별로 지름·면적을 고쳐도 **표는 바뀌고 그림 글자만 옛 값**으로 남았다(확정 10 「구조물별로 다르게도 가능」과 어긋남). """ from __future__ import annotations import json 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_StructureFigure import ( # noqa: E402 build_figure, ) from B08_Quantity.B08_Quantity_Engine_StructureSheet_Edit import ( # noqa: E402 BLINDING_CHOICES, EDITABLE_KEYS, clean_spec, ) from B08_Quantity.B08_Quantity_Engine_UnitQuantity import wants_blinding # noqa: E402 #: 버림이 서는 종류 — 돌쌓기 식(`stone_masonry`)·큰돌쌓기를 타는 것들. BLINDING_TYPES = ("masonry_wet", "masonry_dry", "boulder_masonry", "revetment") def _registry() -> dict[str, dict]: payload = json.loads( (ROOT / "B05_Profile" / "B05_Profile_Structure_Types.json").read_text(encoding="utf-8") ) return {item["type_id"]: item for item in payload["types"]} def test_등록부에_버림_칸이_있다() -> None: """칸이 없으면 저장소가 그 제원을 거절한다(`drop_unregistered` 의 그 자리).""" registry = _registry() for type_id in BLINDING_TYPES: options = {option["key"]: option for option in registry[type_id]["options"]} field = options.get("blinding_concrete") assert field is not None, f"{type_id} 등록부에 버림 칸이 없다" assert field["choices"] == list(BLINDING_CHOICES) # 기본은 **넣음** — 확정 ⑭. 빈 칸도 넣음이지만 화면에 보여야 한다. assert field["default"] == "넣음" assert field["phase"] == "detail" def test_표준도가_버림_칸을_받는다() -> None: assert "blinding_concrete" in EDITABLE_KEYS spec, notes = clean_spec({"blinding_concrete": "안 넣음"}) assert spec == {"blinding_concrete": "안 넣음"} and notes == [] # 빈 값은 「정한 적 없음」 — 키를 지운다(그러면 기본인 「넣음」으로 돈다). spec, _ = clean_spec({"blinding_concrete": ""}) assert spec == {"blinding_concrete": None} # 없는 갈래는 막지 않고 **알린다**(표준도 입력의 규칙). _spec, notes = clean_spec({"blinding_concrete": "빼기도"}) assert notes and "버림 콘크리트" in notes[0] def test_선택지_문구가_엔진이_읽는_말과_같다() -> None: """⚠ 이 시험이 없으면 「제외」처럼 다른 말로 바뀌어도 아무 데서도 안 걸린다.""" 넣음, 안넣음 = BLINDING_CHOICES assert wants_blinding({"blinding_concrete": 넣음}) is True assert wants_blinding({"blinding_concrete": 안넣음}) is False assert wants_blinding({}) is True # 정한 적 없음 = 넣음 def _figure_labels(**options) -> list[str]: sheet = { "key": "masonry_wet", "title": "돌쌓기(찰)", "type_id": "masonry_wet", "height_m": 2.0, "options": {"height_m": 2.0, "back_len_cm": 45, **options}, } return [shape["text"] for shape in build_figure(sheet) or [] if shape["kind"] == "text"] def test_물빼기_주석이_구조물_값을_쓴다() -> None: labels = _figure_labels(weep_hole_diameter_mm=75, weep_hole_area_m2=3) 물구멍 = [text for text in labels if "물구멍" in text] assert 물구멍, labels assert "Ø75" in 물구멍[0] and "3㎡당" in 물구멍[0], 물구멍[0] def test_안_정하면_국가기준_기본이_뜬다() -> None: 물구멍 = [text for text in _figure_labels() if "물구멍" in text] assert 물구멍 and "Ø50" in 물구멍[0] and "2㎡당" in 물구멍[0], 물구멍 def test_제원_칸이_구조물도_탭_표_옆에_있다() -> None: """확정 6 ㉮ 「한 곳에서 정하고 표는 비추기만」. ⓘ 2026-09-13 구조물도가 B08 탭으로 옮겨(PLAN 3장 ④) 표가 CAD 밖 HTML 로 서면서 폼이 **표 바로 옆**에 붙음 — 옛 B07 [여기서 고치기] 단추(표가 CAD 안이라 필요했던 것)는 걷어냄. """ page = (ROOT / "B07_DesignDetail" / "B07_DesignDetail_UI_Page.ts").read_text(encoding="utf-8") assert "specJumpButton" not in page, "B07 에 표준도 제원 칸 흔적이 남았다" sheet = (ROOT / "B08_Quantity" / "B08_Quantity_UI_StructureSheet.ts").read_text( encoding="utf-8" ) assert "buildStandardSpecPanel(" in sheet # 폼에 버림 칸이 섰다 — 끄는 자리가 화면에 있다(확정 8-2). form = (ROOT / "B08_Quantity" / "B08_Quantity_UI_StructureSheet_Spec.ts").read_text( encoding="utf-8" ) assert "blinding_concrete" in form