시험 고침: 534-3 양식 시험의 시트 목록 · 쪽 수 기대(목차 · 부록 더함) · E05 시험 시트 수 7 → 9 · PDF = report_pdf(목차 쪽 번호 = 실제 · 꼬리말 · 머리말) · 부록 절편 · 측점표 쪽 나눔 · 옛 작업본 갱신 시험 덧. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
99 lines
3.2 KiB
Python
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}"
|