Files
Aislo/E05_StabReport/E05_StabReport_Cells.py
T

99 lines
3.2 KiB
Python

"""E05 보고서 칸 쓰기 공용 — 이름표 · 서식 번호 · 칸 값 · 판정 · 기준 안전율 · 측점 이름.
(PLAN 534-3 · 534-5) 빌더 · 대표 단면(`_Section`) · 인쇄(`_Print`)가 같이 씀.
"""
from __future__ import annotations
import re
from typing import Any
CASES = ("dry", "wet", "seismic")
SIDE_NAMES = {"cut": "깎기", "fill": "쌓기"}
METHOD_NAMES = {
"bishop": "Bishop",
"janbu": "Janbu 보정",
"janbu_corrected": "Janbu 보정",
"ordinary": "Fellenius",
"spencer": "Spencer",
}
REASONS = {
"fill_height": "쌓기 높이 10 m 이상",
"cut_height": "깎기 높이 15 m 이상",
"lowest": "최소 안전율 하위",
"user": "사용자 지정",
}
GROUND_NAMES = {"soil": "토사", "ripping_rock": "리핑암", "blasting_rock": "발파암", "fill": "성토"}
ST_TEXT, ST_BODY, ST_FS, ST_NG, ST_OK, ST_NUM1, ST_PROP, ST_CHAIN = 5, 7, 8, 10, 11, 13, 16, 17
A1 = re.compile(r"^([A-Z]+)(\d+)$")
def fig_key(station_key: str) -> str:
return f"fig/{station_key}.png"
UNCONFIRMED = "기본값 · 사용자 확정 전"
def mark(text: str, default: bool) -> str:
"""사용자 확정 전 표기 한 규칙 — 기본값 그대로 = 「(기본값 · 사용자 확정 전)」 · 아니면
「(사용자 입력)」."""
return f"{text} ({UNCONFIRMED if default else '사용자 입력'})"
def _set(sheet: dict[str, Any], a1: str, value: Any, style: int | None = None) -> None:
"""값(None = 지움) · 서식(None = 양식 그대로)."""
cell = dict(sheet["칸"].get(a1) or {})
cell.pop("식", None)
if value is None or value == "":
cell.pop("값", None)
else:
cell["값"] = round(value, 6) if isinstance(value, float) else value
if style is not None:
cell["서식"] = style
if cell:
sheet["칸"][a1] = cell
else:
sheet["칸"].pop(a1, None)
def _copy_row(sheet: dict[str, Any], src: int, dst: int, cols: str) -> None:
for col in cols:
cell = sheet["칸"].get(f"{col}{src}")
if cell and dst != src:
sheet["칸"][f"{col}{dst}"] = {"서식": cell["서식"]} if "서식" in cell else {}
def _shift(a1: str, dr: int) -> str:
m = A1.match(a1)
return f"{m.group(1)}{int(m.group(2)) + dr}"
def _verdict(sheet: dict[str, Any], a1: str, ok: bool | None) -> None:
if ok is None:
_set(sheet, a1, None)
else:
_set(sheet, a1, "O.K" if ok else "N.G", ST_OK if ok else ST_NG)
def _criterion(ground: dict[str, Any], case: str, side: str | None = None) -> float:
sf = (ground.get("safety_factors") or {}).get(case) or {}
if side in ("cut", "fill"):
return float(sf[side])
return max(float(sf["cut"]), float(sf["fill"])) # 표 하나 = 엄한 쪽
def _seismic(ground: dict[str, Any]) -> bool:
return bool((ground.get("seismic") or {}).get("enabled"))
def _labels(stations: list[dict[str, Any]]) -> dict[float, str]:
return {round(float(s["chainage_m"]), 3): s.get("label") or "" for s in stations}
def _label_at(labels: dict[float, str], chainage: float) -> str:
if not labels:
return f"{chainage:.2f}"
near = min(labels, key=lambda c: abs(c - chainage))
return labels[near] or f"{chainage:.2f}"