feat(532-3): E02 지하수 갈래 — 우기 규칙 넷(KDS 기본 · 지표 아래 깊이 비율 · r_u · 사용자 수위선 구간) · 엔진 꼴 바꿈 · 지하수 카드
- ground.json groundwater 칸 덧붙임(wet_mode · depth_ratio · ru · lines · 기존 칸 그대로) · 기본값 = KDS · 범위 칸 넷
- check_ground ↔ checkGround 짝 고침(갈래 · 비율 · r_u · 수위선 구간 · 점) · 거울 시험 판 넷 더함
- groundwater_for · station_groundwater — 측점 모형 → {mode: none | ru | line}(좌 · 우 비탈 높이 따로 · 덮지 않은 측점 = KDS)
- GET · [초기화] = 옛 정본 빠진 칸 기본값으로 채움 · 화면 옛 캐시도 서버 값으로 채움
- 화면 = 지하수 카드 따로(「KDS 기본 · 우기 기준안전율 1.2 와 짝」 안내) · 하중 카드 = 노면 하중 · 지진
- 시험 test_532_4_stab_walls_water(벽 · 지하수) · test_532_3 기본값 지하수 칸 대조 고침(칸 늘어남)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,13 +19,25 @@
|
||||
"short_term": { "cut": 1.1, "fill": 1.1, "soft": 1.1 }
|
||||
},
|
||||
"road_surcharge_kpa": 10.0,
|
||||
"groundwater": { "dry": "none", "wet_soil": "surface", "wet_rock": "half_height" },
|
||||
"groundwater": {
|
||||
"dry": "none",
|
||||
"wet_soil": "surface",
|
||||
"wet_rock": "half_height",
|
||||
"wet_mode": "kds",
|
||||
"depth_ratio": 0.5,
|
||||
"ru": 0.2,
|
||||
"lines": []
|
||||
},
|
||||
"seismic": { "enabled": false, "kh": 0.0 },
|
||||
"limits": {
|
||||
"gamma_t": [10.0, 30.0],
|
||||
"gamma_sat": [10.0, 30.0],
|
||||
"c": [0.0, 1000.0],
|
||||
"phi": [0.0, 60.0],
|
||||
"depth_ratio": [0.0, 1.0],
|
||||
"ru": [0.0, 0.9],
|
||||
"offset_m": [-200.0, 200.0],
|
||||
"water_depth_m": [0.0, 50.0],
|
||||
"safety_factor": [1.0, 3.0],
|
||||
"road_surcharge_kpa": [0.0, 100.0],
|
||||
"kh": [0.0, 0.5],
|
||||
@@ -66,6 +78,10 @@
|
||||
"groundwater.dry": "KDS 11 70 05 표 4.3-3 건기 = 지하수 없음",
|
||||
"groundwater.wet_soil": "KDS 11 70 05 표 4.3-3 우기 토층 · 풍화암 = 지하수위 결정(지표 포화 가정)",
|
||||
"groundwater.wet_rock": "KDS 11 70 05 표 4.3-3 우기 연암 · 경암 = 비탈면 높이 ½ 심도까지 포화",
|
||||
"groundwater.wet_mode": "우기 지하수 규칙 — 기본 = KDS 11 70 05 표 4.3-3(위 두 칸 · 우기 기준안전율 깎기 1.2 와 짝) · 그 밖 갈래는 사용자 판단",
|
||||
"groundwater.depth_ratio": "지표 아래 깊이 = 비탈 높이 × 비율 — 근거 없음 · 사용자 입력(0.5 = KDS 암반 ½ 와 같은 값)",
|
||||
"groundwater.ru": "간극수압비 u = r_u · γ·h — 근거 없음 · 사용자 입력",
|
||||
"groundwater.lines": "사용자 수위선 — 구간(누가 m) · 점(offset m · 공사 뒤 지표 아래 깊이 m) · 덮지 않은 측점은 KDS 규칙",
|
||||
"seismic.enabled": "KDS 11 90 00 1.8.1 ② 영향 범위에 주구조물 없으면 비적용 · 임도는 발주자 협의 — 기본 끔",
|
||||
"seismic.kh": "유사정적 수평지진계수 — 지반가속도의 비율로 사용자 입력(KDS 11 70 05 표 4.3 지진 시)",
|
||||
"stations": "B06 횡단 암 경계(지반선 평행 · 기본 −0.5 m · 물량 가름용 값) — 토사 측점은 경계 없음"
|
||||
|
||||
@@ -79,6 +79,26 @@ CHECK_CASES = [
|
||||
_bad(lambda g: g["groundwater"].update(wet_rock="full")),
|
||||
_bad(lambda g: g.update(seismic={"enabled": "yes", "kh": 0.6})),
|
||||
_bad(lambda g: g.update(label_map=[], safety_factors=None, groundwater="x")),
|
||||
# 지하수 갈래(532-3 덧) — 갈래 · 비율 · r_u · 수위선 구간 · 점
|
||||
_bad(lambda g: g["groundwater"].update(wet_mode="rain", depth_ratio=1.5, ru=None)),
|
||||
_bad(lambda g: g["groundwater"].update(ru=0.95, lines="x")),
|
||||
_bad(
|
||||
lambda g: g["groundwater"].update(
|
||||
wet_mode="line",
|
||||
lines=[
|
||||
{"from_m": 40, "to_m": 20, "points": [[5, 1]]},
|
||||
{"from_m": 0, "to_m": 20, "points": [[5, 1], [5, 2], [300, -1], "p", [1, 2, 3]]},
|
||||
7,
|
||||
],
|
||||
)
|
||||
),
|
||||
_ground(
|
||||
groundwater={
|
||||
**DEFAULTS["groundwater"],
|
||||
"wet_mode": "line",
|
||||
"lines": [{"from_m": 0, "to_m": 20, "points": [[-10, 2], [0, 1.5], [10, 2]]}],
|
||||
}
|
||||
),
|
||||
]
|
||||
|
||||
RESOLVE_CASES = [
|
||||
@@ -124,6 +144,10 @@ def test_기본값_꼴은_Types_그대로():
|
||||
"dry": "none",
|
||||
"wet_soil": "surface",
|
||||
"wet_rock": "half_height",
|
||||
"wet_mode": "kds",
|
||||
"depth_ratio": 0.5,
|
||||
"ru": 0.2,
|
||||
"lines": [],
|
||||
}
|
||||
assert DEFAULTS["seismic"] == {"enabled": False, "kh": 0.0}
|
||||
# 07 §4 종합 성토 φ 28~30 — 브레인 기본 25 는 범위 밖이라 아래값 28
|
||||
@@ -152,6 +176,23 @@ def test_범위_검사():
|
||||
("safety_factors.wet.soft", "range"),
|
||||
}
|
||||
assert codes(CHECK_CASES[17]) == {("seismic.enabled", "enum"), ("seismic.kh", "range")}
|
||||
assert codes(CHECK_CASES[19]) == {
|
||||
("groundwater.wet_mode", "enum"),
|
||||
("groundwater.depth_ratio", "range"),
|
||||
("groundwater.ru", "number"),
|
||||
}
|
||||
assert codes(CHECK_CASES[20]) == {("groundwater.ru", "range"), ("groundwater.lines", "type")}
|
||||
assert codes(CHECK_CASES[21]) == {
|
||||
("groundwater.lines.0.to_m", "order"),
|
||||
("groundwater.lines.0.points", "few_points"),
|
||||
("groundwater.lines.1.points.1.0", "order"),
|
||||
("groundwater.lines.1.points.2.0", "range"),
|
||||
("groundwater.lines.1.points.2.1", "range"),
|
||||
("groundwater.lines.1.points.3", "type"),
|
||||
("groundwater.lines.1.points.4", "type"),
|
||||
("groundwater.lines.2", "type"),
|
||||
}
|
||||
assert codes(CHECK_CASES[22]) == set()
|
||||
|
||||
|
||||
def test_파이썬과_TS_같음():
|
||||
@@ -168,7 +209,7 @@ def test_파이썬과_TS_같음():
|
||||
out = json.loads(proc.stdout)
|
||||
assert out["check"] == [model.check_ground(g, LIMITS) for g in CHECK_CASES]
|
||||
assert out["resolve"] == [model.resolve_stations(g) for g in RESOLVE_CASES]
|
||||
assert all(out["check"][1:]) # 틀린 판은 모두 걸림
|
||||
assert all(out["check"][1:-1]) and out["check"][-1] == [] # 틀린 판은 모두 걸림 · 끝 판 통과
|
||||
|
||||
|
||||
def test_측점_적용():
|
||||
|
||||
@@ -0,0 +1,273 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""532-4 E03 벽(옹벽 · 석축 · 돌쌓기) · 532-3 덧 E02 지하수 갈래.
|
||||
|
||||
- 벽 = B06 화면 TS(`computeStoredLayouts`)를 서버 Node 묶음(`build:e03-walls`)으로 —
|
||||
벽 선 측점 임계면이 벽 폴리곤을 안 지남(모서리 스침 ≤ 0.2 m · 절편 폭 아래) ·
|
||||
벽 없는 측점 = 종전 그대로 · 복사본(구조물 정본 → B06 얹기) 끝까지.
|
||||
- 지하수 = 건기 · 우기 갈래 넷(KDS · 깊이 비율 · r_u · 사용자 수위선) → 엔진 꼴 ·
|
||||
[저장] · [초기화] · 옛 정본 채움.
|
||||
|
||||
./venv/Scripts/python.exe -m pytest resources/tester/test_532_4_stab_walls_water.py -q
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import copy
|
||||
import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
from uuid import UUID
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
from shapely.geometry import LineString, Polygon
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
sys.path.insert(0, str(ROOT))
|
||||
|
||||
from E02_StabGround import E02_StabGround_Model as ground_model # noqa: E402
|
||||
from E02_StabGround import E02_StabGround_Router as router # noqa: E402
|
||||
from E03_StabSection import E03_StabSection_Types as types # noqa: E402
|
||||
from E03_StabSection.E03_StabSection_Engine_Model import section_model_from_b06 # noqa: E402
|
||||
from E03_StabSection.E03_StabSection_Engine_Search import analyze # noqa: E402
|
||||
from E03_StabSection.E03_StabSection_Engine_Station import analyze_station # noqa: E402
|
||||
from E03_StabSection.E03_StabSection_Walls import ( # noqa: E402
|
||||
design_with_walls,
|
||||
walls_by_station,
|
||||
walls_for_sections,
|
||||
walls_of,
|
||||
)
|
||||
|
||||
# 오른쪽(−) 산 · 왼쪽(+) 골 · 노면 −2~2 · 좌 성토 1:1.5(test_532_2 와 같은 단면)
|
||||
GROUND = [(x / 2, 10.0 - 0.6 * (x / 2)) for x in range(-40, 41)]
|
||||
LINE = [(-20.0, 22.0), (-6.0, 13.6), (-2.6, 10.0), (-2.0, 10.0), (2.0, 10.0), (14.0, 2.0)]
|
||||
DESIGN = {
|
||||
"section_mode": "right_cut",
|
||||
"ground_type": "soil",
|
||||
"road_edges": {
|
||||
"left": {"offset_m": 2.0, "elevation_m": 10.0},
|
||||
"right": {"offset_m": -2.0, "elevation_m": 10.0},
|
||||
},
|
||||
"carriageway_edges": {
|
||||
"left": {"offset_m": 1.5, "elevation_m": 10.0},
|
||||
"right": {"offset_m": -1.5, "elevation_m": 10.0},
|
||||
},
|
||||
"cross_slope_pct": 0,
|
||||
"design_elevation_m": 10.0,
|
||||
"design_line": [{"offset_m": a, "elevation_m": b} for a, b in LINE],
|
||||
}
|
||||
CROSS = {
|
||||
"chainage_m": 100.0,
|
||||
"label": "STA.0+100",
|
||||
"samples": [{"offset_m": a, "elevation_m": b, "valid": True} for a, b in GROUND],
|
||||
"design": DESIGN,
|
||||
}
|
||||
WALL = {
|
||||
"structure_id": "w1",
|
||||
"type_id": "masonry_wet",
|
||||
"name": "돌쌓기(찰)",
|
||||
"start_m": 90.0,
|
||||
"end_m": 110.0,
|
||||
"anchor_m": 100.0,
|
||||
"form": "돌쌓기(찰)",
|
||||
"height_m": 3.0,
|
||||
"tiers": 1,
|
||||
"extras": [],
|
||||
}
|
||||
FAST = {"conditions": ("dry",), "methods": ("bishop", "janbu")}
|
||||
CORNER_M = 0.2 # 임계면이 벽 모서리를 스치는 길이 한도(절편 폭 아래)
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def found():
|
||||
return walls_for_sections([{**CROSS, "revetment": WALL}, {**CROSS, "chainage_m": 120.0}])
|
||||
|
||||
|
||||
def test_벽_도형_B06_산식(found):
|
||||
assert list(found) == ["100.00"] # 벽 없는 측점은 안 실음
|
||||
entry = found["100.00"]
|
||||
(wall,) = entry["walls"]
|
||||
assert wall["material_id"] == "wall" and wall["kind"] == "own"
|
||||
poly = Polygon(wall["polygon"])
|
||||
assert poly.is_valid and len(wall["polygon"]) == 4
|
||||
# 높이 = 바닥(근입 0.5 포함) ~ 상단 3 m · 상단 = 노견 높이 · 성토 쪽(좌 +)
|
||||
zs = [p[1] for p in wall["polygon"]]
|
||||
assert max(zs) - min(zs) == pytest.approx(3.0) and max(zs) == pytest.approx(10.0)
|
||||
assert min(p[0] for p in wall["polygon"]) > 0
|
||||
# 지표 = 그려지는 설계선 — 벽 상단(노견)에서 끊겨 바깥은 원지반
|
||||
line = entry["design_line"]
|
||||
gx, gz = zip(*GROUND)
|
||||
out = [(x, z) for x, z in line if x > 2.0 + 1e-3]
|
||||
assert out and all(abs(z - np.interp(x, gx, gz)) < 1e-6 for x, z in out)
|
||||
|
||||
|
||||
def test_임계면이_벽을_안_지남(found):
|
||||
entry = found["100.00"]
|
||||
design = design_with_walls(DESIGN, entry)
|
||||
res = analyze_station(CROSS, design, walls=walls_of(entry), **FAST)
|
||||
poly = Polygon(entry["walls"][0]["polygon"])
|
||||
fill = [c for c in res["cases"] if c["role"] == "fill"]
|
||||
assert fill
|
||||
for case in fill:
|
||||
for got in case["result"]["methods"].values():
|
||||
assert got["min"]["F"] > 0
|
||||
assert LineString(got["min"]["points"]).intersection(poly).length <= CORNER_M
|
||||
plain = analyze_station(CROSS, DESIGN, **FAST)
|
||||
# 벽이 성토 비탈을 대신함 — 성토 쪽 최소 F 가 달라짐 · 절토 쪽은 그대로
|
||||
for a, b in zip(plain["cases"], res["cases"]):
|
||||
if a["role"] == "cut":
|
||||
assert a["min_F"] == pytest.approx(b["min_F"], abs=1e-9)
|
||||
else:
|
||||
assert a["min_F"]["bishop"] != pytest.approx(b["min_F"]["bishop"], abs=1e-3)
|
||||
|
||||
|
||||
def test_벽_없는_측점_모형_그대로():
|
||||
assert design_with_walls(DESIGN, None) is DESIGN and walls_of(None) is None
|
||||
a = section_model_from_b06(CROSS, DESIGN)
|
||||
b = section_model_from_b06(CROSS, design_with_walls(DESIGN, None), walls=walls_of(None))
|
||||
assert json.dumps(a, sort_keys=True) == json.dumps(b, sort_keys=True)
|
||||
|
||||
|
||||
def _write_copy(root: Path, with_wall: bool) -> Path:
|
||||
"""E01 동결 복사본 꼴 — db.json(측점 행 · 노선) · 횡단 파일 · 구조물 정본."""
|
||||
from B05_Profile.B05_Profile_Structures_Repository import save_structures
|
||||
from B05_Profile.B05_Profile_Structures_Schema import StructureInstance
|
||||
|
||||
source = root / "E01_StabRegister" / "source"
|
||||
(source / "B06_Section" / "cross_sections").mkdir(parents=True)
|
||||
(source / "copy_manifest.json").write_text("{}", encoding="utf-8")
|
||||
rows = []
|
||||
for k, chainage in enumerate((80.0, 100.0)):
|
||||
rel = f"B06_Section/cross_sections/cross_{int(chainage):06d}.json"
|
||||
cross = {k: v for k, v in CROSS.items() if k != "design"}
|
||||
cross.update(chainage_m=chainage, center_x=float(chainage), center_y=0.0)
|
||||
(source / rel).write_text(json.dumps(cross), encoding="utf-8")
|
||||
rows.append(
|
||||
{
|
||||
"chainage_m": chainage,
|
||||
"sequence_num": k,
|
||||
"data": json.dumps({"design": DESIGN}),
|
||||
"cross_section_file_path": rel,
|
||||
}
|
||||
)
|
||||
db = {"cross_sections": rows, "route": {"route_data_path": None}}
|
||||
(source / "db.json").write_text(json.dumps(db), encoding="utf-8")
|
||||
if with_wall:
|
||||
item = StructureInstance(
|
||||
type_id="masonry_wet",
|
||||
placement="interval",
|
||||
chainage_m=100,
|
||||
start_m=95,
|
||||
end_m=105,
|
||||
options={"height_m": 3.0},
|
||||
)
|
||||
save_structures(str(source), [item], base_revision=0)
|
||||
return source
|
||||
|
||||
|
||||
def test_복사본_구조물_정본에서_측점별_벽(tmp_path):
|
||||
source = _write_copy(tmp_path / "a", with_wall=True)
|
||||
got = walls_by_station(source)
|
||||
assert list(got) == ["100.00"]
|
||||
(wall,) = got["100.00"]["walls"]
|
||||
assert wall["name"] == "돌쌓기(찰)" and len(wall["polygon"]) == 4
|
||||
assert walls_by_station(_write_copy(tmp_path / "b", with_wall=False)) == {}
|
||||
|
||||
|
||||
# ---- 지하수 갈래 -----------------------------------------------------------------
|
||||
STATIONS = [
|
||||
{"chainage_m": 0.0, "ground_type": "soil", "soil_thickness_m": None},
|
||||
{"chainage_m": 20.0, "ground_type": "blasting_rock", "soil_thickness_m": 0.0},
|
||||
]
|
||||
SURFACE = [[-10.0, 16.0], [-2.0, 10.0], [2.0, 10.0], [10.0, 4.0]]
|
||||
HEIGHTS = {"left": 6.0, "right": 4.0}
|
||||
|
||||
|
||||
def _water(chainage=0.0, condition="wet", **groundwater):
|
||||
ground = ground_model.build_initial(ground_model.load_defaults(), copy.deepcopy(STATIONS))
|
||||
ground["groundwater"].update(groundwater)
|
||||
out = ground_model.groundwater_for(
|
||||
ground, condition, chainage_m=chainage, surface=SURFACE, heights=HEIGHTS
|
||||
)
|
||||
assert types.groundwater(out)["mode"] == out["mode"] # 엔진 꼴 그대로
|
||||
return out
|
||||
|
||||
|
||||
def _depth(out):
|
||||
sx, sz = zip(*SURFACE)
|
||||
return {round(x, 6): round(float(np.interp(x, sx, sz)) - z, 6) for x, z in out["line"]}
|
||||
|
||||
|
||||
def test_지하수_건기_KDS():
|
||||
assert _water(condition="dry") == {"mode": "none"}
|
||||
assert _water(condition="seismic") == {"mode": "none"}
|
||||
assert set(_depth(_water()).values()) == {0.0} # 토사 측점 = 지표 포화
|
||||
rock = _depth(_water(chainage=20.0)) # 연암(겉 = 암 · 두께 0) = 비탈 높이 ½
|
||||
assert rock[10.0] == 3.0 and rock[-10.0] == 2.0
|
||||
|
||||
|
||||
def test_지하수_깊이비율_ru_수위선():
|
||||
ratio = _depth(_water(wet_mode="depth_ratio", depth_ratio=0.25))
|
||||
assert ratio[10.0] == 1.5 and ratio[-10.0] == 1.0 and ratio[2.0] == 1.5
|
||||
assert _water(wet_mode="ru", ru=0.3) == {"mode": "ru", "ru": 0.3}
|
||||
lines = [
|
||||
{"from_m": 0.0, "to_m": 10.0, "points": [[-5.0, 2.0], [5.0, 1.0]]},
|
||||
{"from_m": 0.0, "to_m": 5.0, "points": [[-5.0, 3.0], [5.0, 3.0]]}, # 뒤 구간이 이김
|
||||
]
|
||||
user = _depth(_water(wet_mode="line", lines=lines))
|
||||
assert set(user.values()) == {3.0}
|
||||
one = _depth(_water(wet_mode="line", lines=lines[:1]))
|
||||
assert one[-10.0] == 2.0 and one[2.0] == 1.3 and one[10.0] == 1.0 # 끝은 끝값
|
||||
# 덮지 않은 측점 = KDS
|
||||
assert set(_depth(_water(chainage=20.0, wet_mode="line", lines=lines)).values()) == {2.0, 3.0}
|
||||
|
||||
|
||||
def test_지하수_측점_모형에_끼움():
|
||||
model = section_model_from_b06(CROSS, DESIGN, wet=True)
|
||||
ground = ground_model.build_initial(ground_model.load_defaults(), [])
|
||||
ground["groundwater"].update(wet_mode="depth_ratio", depth_ratio=0.5)
|
||||
gw = ground_model.station_groundwater(ground, "wet", model)
|
||||
left = next(s["height_m"] for s in model["sides"] if s["side"] == "left")
|
||||
sx, sz = zip(*model["surface"])
|
||||
x, z = next(p for p in gw["line"] if p[0] > 1)
|
||||
assert float(np.interp(x, sx, sz)) - z == pytest.approx(0.5 * left)
|
||||
model["groundwater"] = types.groundwater(gw)
|
||||
side = next(s for s in model["sides"] if s["side"] == "left")
|
||||
res = analyze(model, direction=side["direction"], limits=side["limits"], methods=("bishop",))
|
||||
assert res["methods"]["bishop"]["min"]["F"] > 0 # 끼운 모형이 엔진에서 돎
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def project(tmp_path, monkeypatch):
|
||||
async def fake_project(_project_id):
|
||||
return tmp_path, {"company_id": 1, "project_kind": "STABILITY"}
|
||||
|
||||
monkeypatch.setattr(router, "_project", fake_project)
|
||||
monkeypatch.setattr(router, "_b06_stations", lambda _root: copy.deepcopy(STATIONS))
|
||||
return tmp_path
|
||||
|
||||
|
||||
def test_지하수_저장_초기화_옛정본(project):
|
||||
pid = UUID("9872e9a7-e483-427b-a6b2-72cdbc063bc3")
|
||||
session = {"role": "USER", "company_id": 1}
|
||||
first = asyncio.run(router.get_ground(pid, session))
|
||||
ground = copy.deepcopy(first["ground"])
|
||||
assert ground["groundwater"]["wet_mode"] == "kds"
|
||||
line = {"from_m": 0.0, "to_m": 20.0, "points": [[10.0, 2.0], [-10.0, 1.0]][::-1]}
|
||||
ground["groundwater"].update(wet_mode="line", lines=[line], ru=0.35)
|
||||
asyncio.run(router.put_ground(pid, router.GroundRequest(ground=ground)))
|
||||
stored = json.loads(ground_model.ground_path(project).read_text(encoding="utf-8"))
|
||||
assert stored["groundwater"] == {**ground["groundwater"], "lines": [line]}
|
||||
bad = copy.deepcopy(ground)
|
||||
bad["groundwater"]["lines"][0]["points"] = [[1.0, 1.0]]
|
||||
with pytest.raises(router.HTTPException):
|
||||
asyncio.run(router.put_ground(pid, router.GroundRequest(ground=bad)))
|
||||
back = asyncio.run(router.reset_ground(pid))["ground"]
|
||||
assert back["groundwater"]["wet_mode"] == "kds" and back["groundwater"]["lines"] == []
|
||||
# 옛 정본(갈래 칸 없음) = GET 이 기본값으로 채움 · 있는 칸은 그대로
|
||||
old = json.loads(ground_model.ground_path(project).read_text(encoding="utf-8"))
|
||||
old["groundwater"] = {"dry": "none", "wet_soil": "none", "wet_rock": "half_height"}
|
||||
ground_model.ground_path(project).write_text(json.dumps(old), encoding="utf-8")
|
||||
got = asyncio.run(router.get_ground(pid, session))["ground"]["groundwater"]
|
||||
assert got["wet_soil"] == "none" and got["wet_mode"] == "kds" and got["lines"] == []
|
||||
Reference in New Issue
Block a user