- 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>
147 lines
6.1 KiB
Python
147 lines
6.1 KiB
Python
"""E02 지반 조건 API (요구 347-2 · 3 · 4 · PLAN 532-3).
|
|
|
|
- `GET /api/projects/{id}/stab/ground` — 작업본(없으면 초기값) + 기본값 + 고칠 수 있는지
|
|
- `PUT …/stab/ground` — [저장] 검사 뒤 작업본 `E02_StabGround/ground.json`
|
|
· 측점(B06 암 경계)은 서버 값
|
|
- `POST …/stab/ground/reset` — [초기화] 초기값을 작업본에 덮어씀(재계산 아님)
|
|
|
|
초기값 = E 생성 직후 마스터 기본값 + 복사본 B06 측점(`ensure_initial`) · 그 밖에는 쓰지 않음.
|
|
공유 회사(보기만)는 `require_project_access` 가 PUT · POST 를 막음.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
from typing import Any
|
|
from uuid import UUID
|
|
|
|
import aiomysql
|
|
from fastapi import APIRouter, Depends, HTTPException
|
|
from pydantic import BaseModel
|
|
|
|
from B03_FileInput.B03_FileInput_Repository import get_project_storage_relative_path
|
|
from common_util.common_util_auth import verify_session
|
|
from common_util.common_util_json import atomic_write_json
|
|
from common_util.common_util_project_copy import MANIFEST_NAME
|
|
from common_util.common_util_storage import resolve_stored_project_path
|
|
from config.config_db import get_db_pool
|
|
from config.config_system_design import STANDARD_ROCK_BOUNDARY_DEFAULT_OFFSET_M
|
|
from E01_StabRegister.E01_StabRegister_Source import db_snapshot, source_dir
|
|
from E02_StabGround import E02_StabGround_Model as model
|
|
|
|
router = APIRouter(prefix="/api/projects", tags=["E02 Stab Ground"])
|
|
|
|
|
|
class GroundRequest(BaseModel):
|
|
ground: dict[str, Any]
|
|
|
|
|
|
def _b06_stations(project_root: Path) -> list[dict]:
|
|
"""측점 · 지반 라벨 · 토층 두께(암 측점 = 암 경계 깊이 · 토사 = 없음).
|
|
|
|
E01 동결 복사본(`E01_StabRegister/source/db.json`)만 읽음 — 복사본이 없으면 빈 목록.
|
|
"""
|
|
root = source_dir(project_root)
|
|
if not (root / MANIFEST_NAME).is_file():
|
|
return []
|
|
rows = sorted(db_snapshot(root)["cross_sections"], key=lambda r: float(r["chainage_m"] or 0))
|
|
stations = []
|
|
for row in rows:
|
|
data = json.loads(row["data"]) if isinstance(row["data"], str) else row["data"] or {}
|
|
design = data.get("design") or {}
|
|
ground_type = design.get("ground_type") or "soil"
|
|
offset = design.get("rock_boundary_offset_m", STANDARD_ROCK_BOUNDARY_DEFAULT_OFFSET_M)
|
|
rock = ground_type in model.ROCK_LABELS and offset is not None
|
|
stations.append(
|
|
{
|
|
"chainage_m": round(float(row["chainage_m"]), 3),
|
|
"ground_type": ground_type,
|
|
"soil_thickness_m": round(abs(float(offset)), 3) if rock else None,
|
|
}
|
|
)
|
|
return stations
|
|
|
|
|
|
async def _project(project_id: UUID) -> tuple[Path, dict[str, Any]]:
|
|
async with get_db_pool().acquire() as connection:
|
|
try:
|
|
stored = await get_project_storage_relative_path(connection, project_id)
|
|
except Exception as error:
|
|
raise HTTPException(status_code=404, detail="프로젝트를 찾을 수 없습니다.") from error
|
|
async with connection.cursor(aiomysql.DictCursor) as cursor:
|
|
await cursor.execute(
|
|
"SELECT company_id, project_kind FROM projects WHERE id = %s",
|
|
(str(project_id),),
|
|
)
|
|
project = await cursor.fetchone()
|
|
if not project or project["project_kind"] != "STABILITY":
|
|
raise HTTPException(status_code=400, detail="안정성평가 프로젝트가 아닙니다.")
|
|
return Path(resolve_stored_project_path(stored)), project
|
|
|
|
|
|
def ensure_initial(project_root: Path) -> dict:
|
|
"""초기값이 없을 때만 한 번 — 기본값 + 복사본 B06 측점(E 생성 직후 · 옛 E 프로젝트는 첫 GET)."""
|
|
path = model.initial_path(project_root)
|
|
if path.is_file():
|
|
return json.loads(path.read_text(encoding="utf-8"))
|
|
ground = model.build_initial(model.load_defaults(), _b06_stations(project_root))
|
|
atomic_write_json(path, ground)
|
|
return ground
|
|
|
|
|
|
async def seed_initial(project_id: str) -> None:
|
|
"""E01 생성 직후 부름 — 초기값 한 벌."""
|
|
root, _ = await _project(UUID(project_id))
|
|
ensure_initial(root)
|
|
|
|
|
|
def _read_working(project_root: Path) -> dict[str, Any] | None:
|
|
path = model.ground_path(project_root)
|
|
return json.loads(path.read_text(encoding="utf-8")) if path.is_file() else None
|
|
|
|
|
|
def _editable(session: dict[str, Any], project: dict[str, Any]) -> bool:
|
|
return session["role"] == "SYSTEM_ADMIN" or session.get("company_id") == project["company_id"]
|
|
|
|
|
|
@router.get("/{project_id}/stab/ground")
|
|
async def get_ground(
|
|
project_id: UUID, session: dict[str, Any] = Depends(verify_session)
|
|
) -> dict[str, Any]:
|
|
root, project = await _project(project_id)
|
|
initial = ensure_initial(root)
|
|
working = _read_working(root)
|
|
defaults = model.load_defaults()
|
|
return {
|
|
# 옛 정본(지하수 갈래 칸 전) = 빠진 칸만 기본값으로 채워 보냄(파일은 [저장] 때 씀)
|
|
"ground": model.upgrade(working or initial, defaults),
|
|
"saved": working is not None,
|
|
"defaults": defaults,
|
|
"editable": _editable(session, project),
|
|
}
|
|
|
|
|
|
@router.put("/{project_id}/stab/ground")
|
|
async def put_ground(project_id: UUID, payload: GroundRequest) -> dict[str, Any]:
|
|
root, _ = await _project(project_id)
|
|
defaults = model.load_defaults()
|
|
errors = model.check_ground(payload.ground, defaults["limits"])
|
|
if errors:
|
|
raise HTTPException(status_code=422, detail={"errors": errors})
|
|
ground = model.clean(payload.ground)
|
|
# 측점(B06 암 경계)은 서버 값 — 브라우저 값을 받아 적지 않음
|
|
base = _read_working(root) or ensure_initial(root)
|
|
ground["stations"] = base.get("stations", [])
|
|
ground["version"] = defaults["version"]
|
|
atomic_write_json(model.ground_path(root), ground)
|
|
return {"ground": ground}
|
|
|
|
|
|
@router.post("/{project_id}/stab/ground/reset")
|
|
async def reset_ground(project_id: UUID) -> dict[str, Any]:
|
|
root, _ = await _project(project_id)
|
|
initial = ensure_initial(root)
|
|
atomic_write_json(model.ground_path(root), initial)
|
|
return {"ground": model.upgrade(initial, model.load_defaults())}
|