Files
Aislo/resources/tester/test_98_b08_pipe_summary.py
T

227 lines
9.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""B08 구조물 집계표 「횡단배수 | 배관」 실무 꼴 — PLAN 98-1.
2공구 구조물집계표(`4.2 수량산출(2공구).xlsx`) 꼴 견본:
71+13 Φ1200 L22 · 유입 흙막이(찰 H2.0) · 유출 메 H2.0
· 흙막이 1(메 H2.0 · 선 단 1 — 흙막이 2 는 안 섬) · 찰붙임 10 ㎡ → 커플링 올림(22/6)−1 = 3
78+7 Φ800 L18 · 집수정 ㄷ(U) · 유출 메 H2.0 10 m · 메붙임 20 ㎡ → 커플링 2
84+0 Φ800 L16 · 집수정 ㄴ(L) · 유출 메 H2.0 → 커플링 올림(16/6)−1 = 2 · 1본 8 m 면 1
89+10 Φ800 L(없음) · 유출 높이 빈칸 → 관경 최소 1.8 · 집수정 기록 없음 → 알림
"""
from __future__ import annotations
import json
from pathlib import Path
import pytest
from B08_Quantity import B08_Quantity_Router_Summary as summary
from M02_MasterTemplete import M02_Template_Layers as layers
from M02_MasterTemplete.StructOptions import M02_SO_Definition as definition
COMBO = {"layer": "system", "owner": None, "key": "SS000011", "name": "횡단배수 배관"}
OUT = {"layer": "company", "owner": "3", "key": "SC3-000001", "name": "기슭막이 메쌓기"}
WALL = {"side": "outlet", "key": "extra0"}
def _revet(side: str, form: str, height: float | None) -> dict:
found = {
f"{side}_type": "기슭막이",
f"{side}_revet_form": form,
f"{side}_revet_before_m": 5,
f"{side}_revet_after_m": 5,
f"{side}_revet_length_m": 99, # 전 + 후가 앞섬
}
if height is not None:
found[f"{side}_revet_height_m"] = height
return found
def _chute(bond: str, area: float) -> dict:
return {"chute_add_1_structure": "돌붙임", "chute_add_1_bond": bond, "chute_add_1_area": area}
POINTS = [
{
"chainage_m": 1433.0,
"options": {
"pipe_diameter_mm": 1200,
**_revet("inlet", "돌쌓기(찰)", 2.0),
**_revet("outlet", "돌쌓기(메)", 2.0),
"soil_add_1_form": "돌쌓기(메)",
"soil_add_1_height": 2,
"soil_add_1_before": 5,
"soil_add_1_after": 5,
"soil_add_2_form": "돌쌓기(메)",
"soil_add_2_height": 2,
**_chute("찰붙임", 10),
},
"summary_item": COMBO,
"group_items": {"g9lq9uk": OUT},
},
{
"chainage_m": 1567.0,
"options": {
"pipe_diameter_mm": 800,
"inlet_type": "집수정",
"inlet_structure": "auto",
**_revet("outlet", "돌쌓기(메)", 2.0),
**_chute("메붙임", 20),
},
"summary_item": COMBO,
},
{
"chainage_m": 1680.0,
"options": {
"pipe_diameter_mm": "800",
"inlet_type": "집수정",
**_revet("outlet", "돌쌓기(메)", 2.0),
"chute_add_1_structure": "돌붙임", # 붙임 형식 · 면적 빈칸 → 안 셈
},
},
{
"chainage_m": 1790.0,
"options": {"pipe_diameter_mm": 800, "inlet_type": "집수정", **_revet("outlet", "", None)},
},
]
DESIGNS = [
{
"chainage_m": 1433.0,
"design": {"pipe_length_m": 22, "inlet_basin": None, "extra_walls": [WALL]},
},
{"chainage_m": 1567.2, "design": {"pipe_length_m": 18, "inlet_basin": "U", "extra_walls": []}},
{"chainage_m": 1680.0, "design": {"pipe_length_m": 16, "inlet_basin": "L", "extra_walls": []}},
{"chainage_m": 1790.0, "design": {"extra_walls": []}},
]
@pytest.fixture
def root(tmp_path: Path) -> Path:
found = tmp_path / "proj"
edits = found / "B04_PreProcess/drainage/edits"
edits.mkdir(parents=True)
points = [{**p, "source": "user"} for p in POINTS]
(edits / "pipe_points.json").write_text(
json.dumps({"points": points}, ensure_ascii=False), encoding="utf-8"
)
return found
def _run(root: Path, one_m: float | None = 6.0) -> dict:
names = definition.read()["종류"]["pipe"]["이름"]
return summary.pipe_summary(root, DESIGNS, names, one_m)
def _cell(out: dict, at: float, cid: str):
row = next(r for r in out["rows"] if abs(r["chainage_m"] - at) < 0.01)
return row["values"].get(cid)
def test_관경마다_관매설_커플링_관보호공(root):
out = _run(root)
assert _cell(out, 1433, "pipe:len|1200") == 22
assert _cell(out, 1433, "pipe:cp|1200") == 3 # 올림(22/6) − 1
assert _cell(out, 1567, "pipe:len|800") == 18
assert _cell(out, 1567, "pipe:cp|800") == 2
assert _cell(out, 1680, "pipe:cp|800") == 2 # 올림(16/6) − 1
assert _cell(out, 1567, "pipe:basin|800|U") == 1 # 실제로 선 모양(자동 → 지형 규칙 결과)
assert _cell(out, 1680, "pipe:basin|800|L") == 1
assert _cell(out, 1790, "pipe:len|800") is None # 연장 없음 = 빈칸(0 아님)
assert _run(root, 8.0)["rows"][2]["values"]["pipe:cp|800"] == 1 # 1본 8 m: 올림(16/8) − 1
ids = [c["id"] for c in out["columns"]]
assert "pipe:basin|1200|U" not in ids and not any(i.startswith("pipe:basin|1200") for i in ids)
def test_흙막이_형태_높이마다_길이(root):
out = _run(root)
# 71+13: 유입 찰 10 · 유출 메 10 + 흙막이 1 메 10(선 단 1 — 흙막이 2 는 안 셈)
assert _cell(out, 1433, "pipe:revet|돌쌓기(찰)|2.0") == 10
assert _cell(out, 1433, "pipe:revet|돌쌓기(메)|2.0") == 20
# 집수정 유입은 흙막이로 안 셈 · 유출만
assert _cell(out, 1567, "pipe:revet|돌쌓기(찰)|2.0") is None
assert _cell(out, 1567, "pipe:revet|돌쌓기(메)|2.0") == 10
# 빈 높이 = 횡단도 관경 최소(Φ800 → 1.8) · 정의 기본 1.5 아님 · 빈 형태 = 미입력
assert _cell(out, 1790, "pipe:revet|-|1.8") == 10
head = next(c for c in out["columns"] if c["id"] == "pipe:revet|-|1.8")
assert head["group"][1:] == ["미입력", "H=1.8"]
def test_돌붙임은_형식_면적이_다_있을_때만(root):
out = _run(root)
assert _cell(out, 1433, "pipe:chute|찰붙임") == 10
assert _cell(out, 1567, "pipe:chute|메붙임") == 20
assert _cell(out, 1680, "pipe:chute|메붙임") is None
def test_머리_단위_차례_조합_계(root):
out = _run(root)
cols = {c["id"]: c for c in out["columns"]}
assert cols["pipe:len|800"]["group"] == ["관 공", "Φ800m/m", "관매설"]
assert cols["pipe:len|800"]["label"] == "m"
assert cols["pipe:cp|800"]["label"] == "ea"
assert cols["pipe:basin|800|U"]["group"][2] == "관보호공"
assert cols["pipe:basin|800|U"]["label"] == "ㄷ형" # M02 정의 이름(inlet.form=U)
assert cols["pipe:chute|찰붙임"]["label"] == "㎡"
assert cols["pipe:revet|돌쌓기(메)|2.0"]["group"][2] == "H=2.0"
# 차례: 흙막이 → 돌붙임 → 관 공(관경 오름 · 관매설 · 커플링 · 관보호공 I · L · U)
ids = [c["id"] for c in out["columns"]]
assert ids.index("pipe:revet|돌쌓기(찰)|2.0") < ids.index("pipe:revet|돌쌓기(메)|2.0")
assert ids.index("pipe:revet|돌쌓기(메)|2.0") < ids.index("pipe:chute|찰붙임")
assert ids.index("pipe:chute|메붙임") < ids.index("pipe:len|800")
assert ids[ids.index("pipe:len|800") :] == [
"pipe:len|800",
"pipe:cp|800",
"pipe:basin|800|L",
"pipe:basin|800|U",
"pipe:len|1200",
"pipe:cp|1200",
]
# 열마다 조합 id(95-3 모달 · 맨 윗 머리) — 그 값을 낸 컨테이너에 이은 조합
assert cols["pipe:len|800"]["combos"] == ["system||SS000011"]
assert cols["pipe:revet|돌쌓기(메)|2.0"]["combos"] == ["company|3|SC3-000001"]
# 계 줄 = 측점 값 합
assert out["total"]["pipe:len|800"] == 34
assert out["total"]["pipe:cp|800"] == 4
assert out["total"]["pipe:revet|돌쌓기(메)|2.0"] == 40 # 20 + 10 + 10
assert [round(r["chainage_m"]) for r in out["rows"]] == [1433, 1567, 1680, 1790]
def test_알림_연장_집수정_1본(root):
notes = _run(root, None)["notes"]
assert any("관 연장 없음 1곳" in n for n in notes)
assert any("집수정 모양 기록 없음 1곳" in n for n in notes)
assert any("파형강관_1본_m" in n for n in notes)
def test_1본_길이는_프로젝트_층_변수(root, tmp_path, monkeypatch):
system = tmp_path / "master"
(system / "table").mkdir(parents=True)
(system / "table/구조물집계표.json").write_text(
json.dumps({"변수": {"파형강관_1본_m": 8}}), encoding="utf-8"
)
monkeypatch.setattr(layers, "SYSTEM_ROOT", system)
assert summary.one_pipe_length(root) == 8
mine = layers.template_path(layers.project_dir(root), "table", "구조물집계표")
mine.parent.mkdir(parents=True)
mine.write_text(json.dumps({"변수": {"파형강관_1본_m": 6}}), encoding="utf-8")
assert summary.one_pipe_length(root) == 6
mine.write_text(json.dumps({"변수": {"파형강관_1본_m": ""}}), encoding="utf-8")
assert summary.one_pipe_length(root) is None
def test_라우터_등록():
text = (Path(__file__).resolve().parents[2] / "main.py").read_text(encoding="utf-8")
assert "app.include_router(b08_summary_router, dependencies=protected_with_company)" in text
def test_화면이_서버_칸을_그림():
"""98-4 — 화면은 서버 배관 열 · 칸 · 계만 그림(계산 없음) · 배관 조합 열은 서버 열로 바뀜."""
base = Path(__file__).resolve().parents[2]
ui = (base / "B08_Quantity/B08_Quantity_UI_Summary.ts").read_text(encoding="utf-8")
assert "/quantity/summary" in ui and "pipeColumns(pipe, comboPath)" in ui
assert '(point.facility ?? "pipe") !== "pipe"' in ui # 배관 조합 열 대신 서버 열
assert "combosAt(things, station)" in ui # 모달은 배관 조합까지 그대로
locale = (base / "ui_template/ui_template_locale_b4.ts").read_text(encoding="utf-8")
assert 'B08_Quantity_Summary_Total: ["계", "Total"]' in locale