"""E05 보고서 s7 광역요약 — E07 광역 자연사면 위험 저장본 → 시트 (PLAN 536-2). 칸 자리 = `resources/master_template/report/stab_report_slots.md` s7. E07 저장본이 없으면 「광역 해석 전」. """ from __future__ import annotations from pathlib import Path from typing import Any from E02_StabGround.E02_StabGround_Model import load_defaults from E05_StabReport.E05_StabReport_Cells import UNCONFIRMED from E07_StabArea.E07_StabArea_Engine import CLASS_KEYS, LEVEL_KEYS from E07_StabArea.E07_StabArea_Router import RESULT, AreaRequest, load LEVEL_NAMES = {"low": "낮음", "mid": "보통", "high": "높음"} def scenario_text(s: dict[str, Any]) -> str: """시나리오 글 — 화면 시나리오 이름과 같은 말.""" if s.get("scenario") == "saturated": return "최악 — 완전 포화(m = 1 · 강우 극한)" if s.get("scenario") == "sinmap": return ( f"표준 — SINMAP 습윤지수 W = min(R·a / (T·sinθ), 1) · " f"T/R {s['tr_lo']:g}~{s['tr_hi']:g} m" ) return f"옛 조건({s.get('scenario')}) — E07 에서 [다시 해석]" def stamp(project_root: Path) -> list[int] | None: """해석 지문용 — 저장본 크기 · 고친 때.""" path = Path(project_root) / RESULT return [path.stat().st_size, path.stat().st_mtime_ns] if path.is_file() else None def read(project_root: Path) -> dict[str, Any] | None: """저장본에서 보고서에 쓰는 것만(격자 배열 뺌).""" doc = load(project_root) if not doc: return None return {k: doc[k] for k in ("settings", "computed_at", "stations", "summary")} def _set(sheet: dict[str, Any], a1: str, value: Any, style: int | None = None) -> None: cell = dict(sheet["칸"].get(a1) or {}) cell["값"] = round(value, 6) if isinstance(value, float) else value if style is not None: cell["서식"] = style sheet["칸"][a1] = cell UNCONFIRMED_KEYS = ( # (설정 칸, 글) — 기본값 그대로면 사용자 확정 전 ("c_root", "뿌리 점착 {c_root:g} kPa"), ("soil_depth_m", "토층 {soil_depth_m:g} m"), ("tr_lo", "T/R {tr_lo:g}~{tr_hi:g} m"), ("level_lo_pct", "측점 문턱 {level_lo_pct:g} % · {level_hi_pct:g} %"), ) PAIRS = {"tr_lo": "tr_hi", "level_lo_pct": "level_hi_pct"} def _unconfirmed(s: dict[str, Any]) -> str: """기본값 그대로인 설정 · 물성(E02 붕적토 문헌 추정치) — 사용자 확정 전 한 줄.""" fields = AreaRequest.model_fields same = [ text.format(**s) for key, text in UNCONFIRMED_KEYS if all( k in s and float(s[k]) == float(fields[k].default) for k in (key, PAIRS.get(key, key)) ) ] base = {m["id"]: m for m in load_defaults()["materials"]}.get( s.get("material_id") or "colluvium" ) if base and all( float(s.get(k, -1)) == float(base[k]) for k in ("gamma_t", "gamma_sat", "c", "phi") ): same.insert(0, f"토층 물성 = {base['name']} 문헌 추정치") return f"{' · '.join(same)} — {UNCONFIRMED}" if same else "없음(모두 사용자 입력)" def fill_area(sheet: dict[str, Any], area: dict[str, Any] | None, labels: dict[float, str]) -> None: """s7 — 조건 넷 · 등급별 면적 여섯 · 측점 등급별 수 · 보통 · 높음 측점(22 행부터 한 줄씩).""" if not area: _set(sheet, "B5", "광역 해석 전 — E07 광역에서 [해석] 뒤 [보고서 갱신]") return if "station_levels" not in area["summary"]: _set(sheet, "B5", "옛 판 저장본 — E07 광역에서 [다시 해석] 뒤 [보고서 갱신]") return s, summary = area["settings"], area["summary"] _set(sheet, "B5", f"중심선 ± {s['band_m']:g} m · 격자 {s['cell_m']:g} m") _set( sheet, "B6", f"γt {s['gamma_t']:g} · γsat {s['gamma_sat']:g} kN/m³ · c {s['c']:g}~{s['c_hi']:g} kPa · " f"φ {s['phi']:g}~{s['phi_hi']:g}° · 뿌리 점착 {s['c_root']:g} kPa · " f"토층 {s['soil_depth_m']:g} m", ) _set(sheet, "B7", scenario_text(s)) _set(sheet, "B8", str(area["computed_at"]).replace("T", " ")) _set(sheet, "B9", _unconfirmed(s)) for k in range(1, 7): _set(sheet, f"C{11 + k}", float(summary["areas_m2"][CLASS_KEYS[k]])) lo, hi = s["level_lo_pct"], s["level_hi_pct"] bounds = {"low": f"< {lo:g} %", "mid": f"{lo:g} ~ {hi:g} %", "high": f"≥ {hi:g} %"} for col, text in zip("EFG", ("측점 등급", "SI<1 셀 비율", "측점 수")): _set(sheet, f"{col}11", text, 6) for i, key in enumerate(LEVEL_KEYS): _set(sheet, f"E{12 + i}", LEVEL_NAMES[key], 7) _set(sheet, f"F{12 + i}", bounds[key], 7) _set(sheet, f"G{12 + i}", int(summary["station_levels"][key]), 14) _set(sheet, "A20", f"3. 위험 측점 — 보통 · 높음 (영향 구역 안 SI < 1 셀 비율 {lo:g} % 이상)") _set(sheet, "E21", "측점 등급 (SI<1 비율)") rows = [r for r in area["stations"] if r.get("level") in ("mid", "high")] template = {col: dict(sheet["칸"].get(f"{col}22") or {}) for col in "ABCDEFG"} if not rows: _set(sheet, "A22", "위험 측점 없음") for i, row in enumerate(rows): r = 22 + i for col, cell in template.items(): sheet["칸"][f"{col}{r}"] = dict(cell) ch = float(row["chainage_m"]) _set(sheet, f"A{r}", row.get("label") or labels.get(round(ch, 3)) or f"{ch:.2f}") _set(sheet, f"B{r}", ch) _set(sheet, f"C{r}", float(row["fs_min"])) _set(sheet, f"D{r}", float(row["si_min"])) _set(sheet, f"E{r}", f"{LEVEL_NAMES[row['level']]} ({row['risk_ratio'] * 100:.1f} %)") _set(sheet, f"F{r}", float(row["risk_area_m2"])) _set(sheet, f"G{r}", float(row["slope_max_deg"]))