Merge remote-tracking branches 'origin/sub_laptop_2', 'origin/sub_laptop_3' and 'origin/sub_laptop_8' into sub_laptop_4
This commit is contained in:
@@ -68,7 +68,8 @@ export async function renderD02PlanBase(root: HTMLElement): Promise<void> {
|
||||
const table = el("div", { className: "d02-points" });
|
||||
const info = el("p", { className: "d02-info" });
|
||||
const card = createPanelCard(t("D02_Points_Title"), false);
|
||||
card.append(el("p", { className: "d02-info", text: t("D02_Points_Hint") }), table, info);
|
||||
const pointsHint = el("p", { className: "d02-info", text: t("D02_Points_Hint") });
|
||||
card.append(pointsHint, table, info);
|
||||
const save = createButton({
|
||||
label: t("D02_Save"),
|
||||
variant: "filled",
|
||||
@@ -240,6 +241,11 @@ export async function renderD02PlanBase(root: HTMLElement): Promise<void> {
|
||||
try {
|
||||
editable = !!(await fetchSessionUser())?.is_forest_service;
|
||||
save.hidden = !editable;
|
||||
if (!editable) {
|
||||
const mapHint = map.root.querySelector<HTMLElement>("[data-kakao-map-hint]");
|
||||
pointsHint.textContent = t("D02_View_Only");
|
||||
if (mapHint) mapHint.textContent = t("D02_View_Only");
|
||||
}
|
||||
adopt(await request<PointsView>(`/projects/${projectId}/plan/points`));
|
||||
const cached = JSON.parse(sessionStorage.getItem(cacheKey(projectId)) ?? "null") as {
|
||||
points?: Array<LonLat | null>;
|
||||
|
||||
@@ -18,7 +18,7 @@ import math
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import Any, Literal
|
||||
from functools import lru_cache
|
||||
from functools import lru_cache, partial
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
@@ -100,6 +100,7 @@ def _context(root: Path, station_key: str) -> dict[str, Any]:
|
||||
walls = walls_mod.walls_of(entry)
|
||||
if walls:
|
||||
kw["walls"] = walls
|
||||
kw["groundwater_for"] = partial(ground_model.station_groundwater, ground)
|
||||
return {
|
||||
"ground": ground,
|
||||
"plan": plan,
|
||||
|
||||
@@ -13,10 +13,12 @@ import json
|
||||
import logging
|
||||
import sys
|
||||
from concurrent.futures import ProcessPoolExecutor, as_completed
|
||||
from functools import partial
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable
|
||||
|
||||
from common_util.common_util_derived_store import write_derived
|
||||
from E02_StabGround.E02_StabGround_Model import station_groundwater
|
||||
from E03_StabSection.E03_StabSection_Engine_Station import analyze_station
|
||||
|
||||
KIND_STATION = "stab2d_station"
|
||||
@@ -29,6 +31,8 @@ def _analyze(
|
||||
kw = {**kwargs, "layers": item["layers"], "ground": [tuple(p) for p in item["ground"]]}
|
||||
if item.get("walls"):
|
||||
kw["walls"] = item["walls"]
|
||||
if conditions_ground is not None:
|
||||
kw["groundwater_for"] = partial(station_groundwater, conditions_ground)
|
||||
return analyze_station(item["cross"], item["design"], **kw)
|
||||
|
||||
|
||||
|
||||
@@ -3,13 +3,14 @@
|
||||
입력 = E02 지반 조건(`ground.json`) · E03 2D 파생 저장본(`stab2d_summary` 가장 최근 · 측점별
|
||||
`stab2d_station`) · 대표 단면(E03 자동 + `representative.json` 켬/끔) · E04 3D 저장본 요약
|
||||
(있으면) · E07 광역 저장본(있으면) · E03 보강 검토 저장본(있으면 · 없으면 s8 뺌) ·
|
||||
E06 암반 해석 저장본(있으면 · 없으면 s11 뺌) ·
|
||||
E01 복사본(측점 · 횡단 · 노선).
|
||||
양식 = `resources/master_template/report/stab_report.json`
|
||||
칸 자리 = 같은 폴더 `stab_report_slots.md`.
|
||||
|
||||
몫: 사용자(표지 s1 · 개요 s2) = 처음 만들 때만 채움 ·
|
||||
프로그램(목차 s9 · 입력조건 s3 · 측점표 s4 · 대표단면 s5 · 3D요약 s6 · 광역요약 s7 · 보강검토 s8 ·
|
||||
부록_절편 s10) = [보고서 갱신] 때 양식에서 통째로 다시 씀. 대표 단면 · 부록 = `_Section` ·
|
||||
암반검토 s11 · 부록_절편 s10) = [보고서 갱신] 때 양식에서 통째로 다시 씀. 대표 단면 · 부록 = `_Section` ·
|
||||
목차 쪽 번호 · 머리말/꼬리말 · 긴 표 쪽 나눔 = `_Print` · 칸 공용 = `_Cells`.
|
||||
판정 글 칸 = 엔진 판정 값 + N.G 서식 10 · O.K 서식 11 을 직접 붙임
|
||||
(인쇄 HTML · xlsx 는 조건부 서식을 안 먹음).
|
||||
@@ -38,6 +39,7 @@ from E05_StabReport import E05_StabReport_Area as area
|
||||
from E05_StabReport import E05_StabReport_Figure as figure
|
||||
from E05_StabReport import E05_StabReport_Print as printer
|
||||
from E05_StabReport import E05_StabReport_Reinforce as reinforce
|
||||
from E05_StabReport import E05_StabReport_Rock as rock
|
||||
from E05_StabReport import E05_StabReport_Section as section
|
||||
from E05_StabReport.E05_StabReport_Cells import ( # noqa: F401 — 시험 · 다른 모듈이 build.* 로 씀
|
||||
A1,
|
||||
@@ -60,7 +62,7 @@ FORM_PATH = ROOT / "resources" / "master_template" / "report" / "stab_report.jso
|
||||
STAGE = "E05_StabReport"
|
||||
REPORT, META = "report.json", "report_meta.json"
|
||||
USER_SHEETS = ("s1", "s2")
|
||||
PROGRAM_SHEETS = ("s9", "s3", "s4", "s5", "s6", "s7", "s8", "s10")
|
||||
PROGRAM_SHEETS = ("s9", "s3", "s4", "s5", "s6", "s7", "s8", "s11", "s10")
|
||||
REPRESENTATIVE = Path("E03_StabSection") / "representative.json"
|
||||
BAND_KEY = "fig/3d_band.png"
|
||||
|
||||
@@ -167,6 +169,7 @@ def analysis_fingerprint(project_root: Path) -> str:
|
||||
else [],
|
||||
"area": area.stamp(project_root),
|
||||
"reinforce": reinforce.stamp(project_root),
|
||||
"rock": rock.stamp(project_root),
|
||||
"form": hashlib.sha1(FORM_PATH.read_bytes()).hexdigest(),
|
||||
}
|
||||
text = json.dumps(body, ensure_ascii=False, sort_keys=True, default=str)
|
||||
@@ -217,6 +220,7 @@ def gather(project_root: Path) -> dict[str, Any]:
|
||||
"sections3d": sorted(sections3d, key=lambda s: s["start_m"]),
|
||||
"area": area.read(project_root),
|
||||
"reinforce": reinforce.read(project_root),
|
||||
"rock": rock.read(project_root),
|
||||
"fingerprint": analysis_fingerprint(project_root),
|
||||
}
|
||||
|
||||
@@ -445,6 +449,10 @@ def build_program(book: dict[str, Any], inputs: dict[str, Any]) -> list[tuple[st
|
||||
reinforce.fill_reinforce(by["s8"], inputs["reinforce"], _labels(inputs["stations"]))
|
||||
else: # 보강 검토가 없으면 시트를 뺌
|
||||
book["시트"] = [s for s in book["시트"] if s["id"] != "s8"]
|
||||
if inputs.get("rock"):
|
||||
rock.fill_rock(by["s11"], inputs["rock"], _labels(inputs["stations"]))
|
||||
else: # 암반 해석이 없으면 시트를 뺌
|
||||
book["시트"] = [s for s in book["시트"] if s["id"] != "s11"]
|
||||
printer.fill_toc(by["s9"], book, None) # 쪽 번호 = 저장 전 `printer.number_toc`
|
||||
return jobs
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ TITLES = {
|
||||
"s6": "3D 해석 요약",
|
||||
"s7": "광역 자연사면 위험 요약",
|
||||
"s8": "보강 검토 — 비탈 완화 · 소단 · 벽 외적 안정",
|
||||
"s11": "암반 비탈면 검토 — 평사투영 · 평면 · 쐐기 안전율",
|
||||
"s10": "대표 단면 절편 계산표",
|
||||
}
|
||||
APPENDIX = {"s10"}
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
"""E05 보고서 s11 암반검토 — E06 암반 해석 저장본(절리 · Markland · Hoek-Bray) → 시트 (PLAN 536-1).
|
||||
|
||||
칸 자리 = `resources/master_template/report/stab_report_slots.md` s11.
|
||||
저장본이 없으면 시트를 뺌(있을 때만) — `build_program` 이 책에서 지움.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from fastapi import HTTPException
|
||||
|
||||
from E05_StabReport.E05_StabReport_Cells import _set, _verdict, mark
|
||||
from E06_StabRock import E06_StabRock_Model as rock_model
|
||||
from E06_StabRock.E06_StabRock_Router import _result
|
||||
|
||||
CONDITION_NAMES = {"dry": "건기", "wet": "우기", "seismic": "지진"}
|
||||
SIDE_NAMES = {"left": "좌", "right": "우"}
|
||||
PROJECTION_NAMES = {"equal_angle": "등각(Wulff)", "equal_area": "등면적(Schmidt)"}
|
||||
VERDICT_NAMES = {"NG": "N.G", "check": "검토", "OK": "O.K", "none": "위험 모드 없음"}
|
||||
SLIDE_NAMES = {"1": "한 면({a}) 미끄러짐", "2": "한 면({b}) 미끄러짐", "12": "두 면 미끄러짐"}
|
||||
COLS = "ABCDEFGH"
|
||||
FIRST = 11 # 이 행부터 절리 · 측점 표를 다시 깔음
|
||||
|
||||
|
||||
def read(project_root: Path) -> dict[str, Any] | None:
|
||||
"""보고서에 쓰는 E06 결과(지금 입력 지문 것 · 없으면 가장 최근) + 낡음 표시."""
|
||||
try:
|
||||
out = _result(Path(project_root), None, None)
|
||||
except HTTPException:
|
||||
return None
|
||||
return {**out["result"], "stale": out["stale"]} if out["result"] else None
|
||||
|
||||
|
||||
def stamp(project_root: Path) -> list | None:
|
||||
"""해석 지문용 — 결과 지문 · 낡음."""
|
||||
rock = read(project_root)
|
||||
return [rock["fingerprint"], rock["stale"]] if rock else None
|
||||
|
||||
|
||||
def _row_styles(sheet: dict[str, Any], r: int) -> dict[str, dict]:
|
||||
return {c: copy.deepcopy(sheet["칸"].get(f"{c}{r}") or {}) for c in COLS}
|
||||
|
||||
|
||||
def _summary(rock: dict[str, Any]) -> str:
|
||||
s = rock["summary"]
|
||||
v = s["verdicts"]
|
||||
text = (
|
||||
f"깎기 비탈 {s['slopes']} · "
|
||||
+ " · ".join(f"{VERDICT_NAMES[k]} {v[k]}" for k in VERDICT_NAMES if v.get(k))
|
||||
+ f" — 위험 모드 평면 {s['modes']['planar']} · 쐐기 {s['modes']['wedge']} · "
|
||||
f"전도 {s['modes']['toppling']}"
|
||||
)
|
||||
if rock["stale"]:
|
||||
text += " · 입력이 해석 뒤 바뀜 — E06 [해석] 뒤 [보고서 갱신]"
|
||||
return text
|
||||
|
||||
|
||||
def fill_rock(sheet: dict[str, Any], rock: dict[str, Any], labels: dict) -> None:
|
||||
"""s11 — 1. 조건 · 요약(5~9 행) · 2. 절리군(한 줄씩 · 13 행 서식) · 3. 측점(17 행 서식)."""
|
||||
s = rock["settings"]
|
||||
d = rock_model.DEFAULT_SETTINGS
|
||||
lateral = (s["lateral_planar_deg"], s["lateral_toppling_deg"])
|
||||
_set(
|
||||
sheet,
|
||||
"B5",
|
||||
mark(
|
||||
f"{PROJECTION_NAMES.get(s['projection'], s['projection'])} 하반구 · "
|
||||
f"측방 한계 평면 {lateral[0]:g}° · 전도 {lateral[1]:g}°",
|
||||
lateral == (d["lateral_planar_deg"], d["lateral_toppling_deg"]),
|
||||
),
|
||||
)
|
||||
_set(
|
||||
sheet,
|
||||
"B6",
|
||||
f"{s['gamma_r']:g} kN/m³ (사용자 입력)"
|
||||
if s.get("gamma_r")
|
||||
else "E02 측점 암 물성 γt (문헌 추정치 · 사용자 확정 전)",
|
||||
)
|
||||
_set(
|
||||
sheet,
|
||||
"B7",
|
||||
mark(
|
||||
f"인장균열 깊이 · 활동면 높이의 {s['water_fill_wet']:g} 까지 지하수 "
|
||||
"(KDS 11 70 05 표 4.3-3 암반 = ½ 심도)",
|
||||
s["water_fill_wet"] == d["water_fill_wet"],
|
||||
),
|
||||
)
|
||||
crack = s["crack"]
|
||||
crack_text = {
|
||||
"none": "인장균열 없음",
|
||||
"critical": "인장균열 = 임계(최소 안전율 자리)",
|
||||
"depth": f"인장균열 깊이 {crack.get('z') or 0:g} m",
|
||||
}[crack["mode"]]
|
||||
bolt = s["bolt"]
|
||||
bolt_text = (
|
||||
f"볼트 {bolt['force']:g} (평면 kN/m · 쐐기 kN) · 수평 아래 {bolt['plunge']:g}°"
|
||||
if bolt["force"]
|
||||
else "볼트 없음"
|
||||
)
|
||||
_set(sheet, "B8", f"{crack_text} · {bolt_text}")
|
||||
_set(sheet, "B9", _summary(rock))
|
||||
|
||||
tpl = {r: _row_styles(sheet, r) for r in (11, 12, 13, 15, 16, 17)}
|
||||
merged = [m for m in sheet["병합"] if m.startswith(("A11:", "A15:"))]
|
||||
for a1 in [k for k in sheet["칸"] if int("".join(ch for ch in k if ch.isdigit())) >= FIRST]:
|
||||
del sheet["칸"][a1]
|
||||
sheet["병합"] = [m for m in sheet["병합"] if m not in merged]
|
||||
heights = {k: v for k, v in sheet["행"].items() if int(k) >= FIRST}
|
||||
sheet["행"] = {k: v for k, v in sheet["행"].items() if int(k) < FIRST}
|
||||
|
||||
def put_row(r: int, src: int) -> None:
|
||||
for c, cell in tpl[src].items():
|
||||
if cell:
|
||||
sheet["칸"][f"{c}{r}"] = copy.deepcopy(cell)
|
||||
if str(src) in heights:
|
||||
sheet["행"][str(r)] = dict(heights[str(src)])
|
||||
|
||||
def head(r: int, src: int) -> int:
|
||||
put_row(r, src)
|
||||
sheet["병합"].append(f"A{r}:H{r}")
|
||||
put_row(r + 1, src + 1)
|
||||
return r + 2
|
||||
|
||||
# 2. 절리군
|
||||
r = head(FIRST, 11)
|
||||
sets = _sets(rock)
|
||||
for j in sets:
|
||||
put_row(r, 13)
|
||||
_set(sheet, f"A{r}", j.get("name") or j["id"])
|
||||
for col, key in zip("BCDEF", ("dip_direction", "dip", "phi", "c", "persistence_m")):
|
||||
value = j.get(key)
|
||||
_set(sheet, f"{col}{r}", float(value) if value is not None else "-")
|
||||
_set(sheet, f"G{r}", _station_text(labels, j.get("from_m")))
|
||||
_set(sheet, f"H{r}", _station_text(labels, j.get("to_m")))
|
||||
r += 1
|
||||
if not sets:
|
||||
put_row(r, 13)
|
||||
_set(sheet, f"A{r}", "절리군 없음")
|
||||
r += 1
|
||||
|
||||
# 3. 측점별 판정
|
||||
r = head(r + 1, 15)
|
||||
for entry in rock["stations"]:
|
||||
r = _station_rows(sheet, put_row, r, entry, labels)
|
||||
|
||||
|
||||
def _sets(rock: dict[str, Any]) -> list[dict[str, Any]]:
|
||||
"""해석에 쓴 절리군 — 옛 저장본(sets 없음)은 측점 결과에 실린 것(지속성 · 구간 없음)."""
|
||||
if "sets" in rock:
|
||||
return rock["sets"]
|
||||
seen: dict[str, dict] = {}
|
||||
for entry in rock["stations"]:
|
||||
for j in entry["joints"]:
|
||||
seen.setdefault(j["id"], j)
|
||||
return list(seen.values())
|
||||
|
||||
|
||||
def _station_text(labels: dict, chainage: Any) -> Any:
|
||||
if chainage is None:
|
||||
return "-"
|
||||
ch = float(chainage)
|
||||
return labels.get(round(ch, 3)) or f"{ch:.2f}"
|
||||
|
||||
|
||||
def _station_rows(sheet, put_row, r: int, entry: dict[str, Any], labels: dict) -> int:
|
||||
slope = entry["slope"]
|
||||
ch = float(entry["chainage_m"])
|
||||
name = entry.get("label") or labels.get(round(ch, 3)) or f"{ch:.2f}"
|
||||
side = (
|
||||
f"{SIDE_NAMES.get(slope['side'], slope['side'])} · {slope['dip']:g}° / "
|
||||
f"{slope['dip_direction']:g}° · 높이 {slope['height_m']:.1f} m"
|
||||
)
|
||||
jname = {j["id"]: j.get("name") or j["id"] for j in entry["joints"]}
|
||||
rows: list[tuple[str, str, Any, Any, bool | None, str]] = []
|
||||
for case in entry["cases"]:
|
||||
ids = [jname.get(i, i) for i in case["ids"]]
|
||||
mode = f"평면 {ids[0]}" if case["mode"] == "planar" else f"쐐기 {ids[0]} × {ids[1]}"
|
||||
notes = []
|
||||
if not case["formed"]:
|
||||
notes.append("블록 안 생김")
|
||||
if case.get("sliding") == "lift":
|
||||
notes.append("들림")
|
||||
elif case.get("sliding") in SLIDE_NAMES:
|
||||
notes.append(SLIDE_NAMES[case["sliding"]].format(a=ids[0], b=ids[-1]))
|
||||
if case.get("persistence_short"):
|
||||
notes.append("지속성 < 활동면")
|
||||
cond = CONDITION_NAMES.get(case["condition"], case["condition"])
|
||||
rows.append(
|
||||
(mode, cond, case["F"], case.get("required"), case.get("ok"), " · ".join(notes))
|
||||
)
|
||||
for jid in entry["kinematic"]["toppling"]:
|
||||
rows.append(
|
||||
(f"전도 {jname.get(jid, jid)}", "-", None, None, None, "기하 판정만 — 현장 확인")
|
||||
)
|
||||
if not rows:
|
||||
rows.append(("없음", "-", None, None, None, "위험 모드 없음(Markland)"))
|
||||
for mode, cond, fs, need, ok, note in rows:
|
||||
put_row(r, 17)
|
||||
_set(sheet, f"A{r}", name)
|
||||
_set(sheet, f"B{r}", side)
|
||||
_set(sheet, f"C{r}", mode)
|
||||
_set(sheet, f"D{r}", cond)
|
||||
_set(sheet, f"E{r}", float(fs) if fs is not None else "-")
|
||||
_set(sheet, f"F{r}", float(need) if need is not None else "-")
|
||||
if ok is None:
|
||||
_set(sheet, f"G{r}", "검토" if mode.startswith("전도") else "-")
|
||||
else:
|
||||
_verdict(sheet, f"G{r}", ok)
|
||||
_set(sheet, f"H{r}", note or None)
|
||||
r += 1
|
||||
return r
|
||||
@@ -165,6 +165,7 @@ def _run(root: Path) -> dict[str, Any]:
|
||||
value = {
|
||||
"fingerprint": fp,
|
||||
"settings": joints["settings"],
|
||||
"sets": joints["sets"],
|
||||
"summary": _summary(stations),
|
||||
"stations": stations,
|
||||
}
|
||||
|
||||
@@ -46,5 +46,5 @@
|
||||
- 측점의 B06 지반 · B06 토층 두께는 복사본 값이라 화면에서 고칠 수 없음 — 바꾸려면 구간 덮어쓰기를 쓸 것.
|
||||
- 저장 뒤 E03 에서 [해석]을 다시 누르면 값이 바뀐 측점만 다시 계산함.
|
||||
- 기준안전율 표의 「단기」 줄 · 「쌓기(연약지반)」 칸은 값만 보관함 — 지금 2D 해석 판정은 건기 · 우기 · 지진 시의 깎기 · 쌓기 칸만 씀.
|
||||
- 우기 지하수 규칙(KDS 기본 외 갈래 포함)은 지금 E03 횡단 그림의 수위선에 반영됨. 안전율 계산은 아직 모든 측점을 「공사 뒤 지표까지 포화」로 셈 — 고른 갈래의 계산 반영은 준비 중.
|
||||
- 지하수 규칙(건기 · 우기 갈래 넷)은 E03 안전율 계산과 횡단 그림의 수위선에 함께 반영됨. 규칙을 바꾸면 E03 에서 다시 [해석].
|
||||
- 칸 뜻 · 범위는 「칸 뜻」, 근거 · 수식은 「쓰인 방법과 근거」, 확정 안 된 값 목록은 「사용자 확정 전 값」 항목에 있음.
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
4. **사용자 수위선** — 구간마다 점(offset · 지표 아래 깊이)을 이은 선. 점 사이는 곧은 선. 덮지 않은 측점은 KDS 기본 규칙.
|
||||
|
||||
- 건기 · 지진 시는 「건기 지하수」 규칙(기본 없음)을 씀.
|
||||
- 지금 E03 안전율 계산은 우기를 모든 측점 「공사 뒤 지표까지 포화」로 셈 — 위 규칙은 횡단 그림의 수위선에만 반영됨(계산 반영은 준비 중). 포화는 가장 불리한 쪽 가정임.
|
||||
- 위 규칙은 E03 안전율 계산과 횡단 그림의 수위선에 같이 반영됨. 지표까지 포화는 가장 불리한 쪽 가정임.
|
||||
|
||||
## 노면 하중
|
||||
|
||||
|
||||
@@ -54,5 +54,5 @@
|
||||
|
||||
## 계산에 아직 안 들어간 값
|
||||
|
||||
- 우기 규칙(KDS 기본의 암반 ½ · 깊이 비율 · r_u · 사용자 수위선)은 지금 E03 횡단 그림의 수위선에만 보임. 안전율 계산은 모든 측점을 「공사 뒤 지표까지 포화」로 셈 — 계산 반영은 준비 중.
|
||||
- 우기 규칙(KDS 기본의 암반 ½ · 깊이 비율 · r_u · 사용자 수위선)은 E03 안전율 계산과 횡단 그림의 수위선에 같이 쓰임. 3D(E04)는 따로 「지표까지 포화」 한 가지.
|
||||
- 기준안전율 「단기」 줄 · 「쌓기(연약지반)」 칸은 2D 판정에 아직 안 쓰임.
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
|
||||
- **문헌 추정치 · 현장 시험 없음**: 지반 값이 문헌 대표값이라 안전율은 설계 검토용 참고값임. 현장 조사 값으로 바꿔 다시 해석할 것.
|
||||
- 해석은 서버가 함 · 화면은 결과만 보임. 같은 입력이면 다시 셈하지 않음.
|
||||
- 우기 계산 = 지금은 모든 측점 「공사 뒤 지표까지 포화」. E02 에서 고른 우기 규칙은 횡단 그림의 지하수위 점선에만 반영됨(계산 반영은 준비 중). r_u 를 골랐으면 그림 아래에 「지하수 = 간극수압비 r_u …」 글만 보임.
|
||||
- 지하수 = E02 에서 고른 규칙(건기 · 우기 갈래 넷)을 안전율 계산과 지하수위 점선에 같이 씀. r_u 를 골랐으면 수위선 없이 그림 아래에 「지하수 = 간극수압비 r_u …」 글만 보임.
|
||||
- 탐색 범위 = 노면 반대쪽 끝 ~ 비탈 끝 바깥 (5 m 와 비탈 높이 2 배 중 큰 값)까지 — 설계 비탈을 지나는 면만 봄. 노선 밖 자연 비탈 전체 붕괴는 광역 위험(E07) 몫.
|
||||
- 깊이 0.3 m 미만으로 겉만 긁는 면은 붕괴로 보지 않음.
|
||||
- 벽은 아주 단단한 물체로 넣어 활동면이 벽을 지나지 않음 — 벽 자체 안정은 보강 검토의 「벽 외적 안정」으로 봄.
|
||||
|
||||
@@ -41,9 +41,9 @@
|
||||
|
||||
| 조건 | 지하수 · 하중 | 기준안전율(E02 기본 · 깎기 / 쌓기) |
|
||||
| --- | --- | --- |
|
||||
| 건기 | 지하수 없음 | 1.5 / 1.5 |
|
||||
| 우기 | 지금은 모든 측점 공사 뒤 지표까지 포화 | 1.2 / 1.3 |
|
||||
| 지진(E02 에서 켰을 때) | 건기 수위 + 수평지진계수 kh | 1.1 / 1.1 |
|
||||
| 건기 | E02 건기 지하수 규칙(기본 없음) | 1.5 / 1.5 |
|
||||
| 우기 | E02 우기 규칙(KDS 기본 = 토사 · 풍화암 지표 포화 · 암반 비탈 높이 ½) | 1.2 / 1.3 |
|
||||
| 지진(E02 에서 켰을 때) | E02 건기 지하수 규칙 + 수평지진계수 kh | 1.1 / 1.1 |
|
||||
|
||||
- 기준 = KDS 11 70 05 표 4.3-1 · 4.3-2 · 4.3-3(지식DB 04 §2). 값은 E02 에서 바꿀 수 있음.
|
||||
- 좌 · 우 비탈을 따로 셈 — 깎기 = 산 쪽에서 노면 쪽으로 미끄러짐 · 쌓기 = 노면에서 바깥쪽으로 미끄러짐.
|
||||
@@ -75,7 +75,7 @@
|
||||
|
||||
- 지반 값 = 문헌 추정치 · 현장 시험 없음.
|
||||
- 2D 해석이라 비탈 양옆이 붙잡아 주는 효과(끝단 효과)는 빠짐 — 3D 해석(E04) 몫.
|
||||
- 우기는 지표까지 포화로 셈(가장 불리한 쪽). E02 우기 규칙 갈래(암반 ½ · 깊이 비율 · r_u · 수위선)의 계산 반영은 준비 중.
|
||||
- 우기 지하수는 E02 규칙대로(KDS 기본 · 깊이 비율 · r_u · 수위선) — 지표까지 포화는 가장 불리한 쪽.
|
||||
- 강우 침투 해석(시간에 따른 수위 변화) · 인장균열은 넣지 않음.
|
||||
- 암반 절리를 따라 미끄러지는 평면 · 쐐기 파괴는 암반 해석(E06) 몫.
|
||||
- 네일 · 앵커 보강 가정은 준비 중.
|
||||
|
||||
@@ -3687,6 +3687,452 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "s11",
|
||||
"이름": "암반검토",
|
||||
"칸": {
|
||||
"A1": {
|
||||
"값": "암반 비탈면 검토 (평사투영 · 한계평형)",
|
||||
"서식": 1
|
||||
},
|
||||
"B1": {
|
||||
"서식": 1
|
||||
},
|
||||
"C1": {
|
||||
"서식": 1
|
||||
},
|
||||
"D1": {
|
||||
"서식": 1
|
||||
},
|
||||
"E1": {
|
||||
"서식": 1
|
||||
},
|
||||
"F1": {
|
||||
"서식": 1
|
||||
},
|
||||
"G1": {
|
||||
"서식": 1
|
||||
},
|
||||
"H1": {
|
||||
"서식": 1
|
||||
},
|
||||
"A2": {
|
||||
"값": "절리 = 현장 조사값(사용자 입력) · Markland 운동학 판정 · Hoek-Bray 평면 · 쐐기 안전율(KDS 11 70 05) · 문헌 추정치",
|
||||
"서식": 2
|
||||
},
|
||||
"B2": {
|
||||
"서식": 2
|
||||
},
|
||||
"C2": {
|
||||
"서식": 2
|
||||
},
|
||||
"D2": {
|
||||
"서식": 2
|
||||
},
|
||||
"E2": {
|
||||
"서식": 2
|
||||
},
|
||||
"F2": {
|
||||
"서식": 2
|
||||
},
|
||||
"G2": {
|
||||
"서식": 2
|
||||
},
|
||||
"H2": {
|
||||
"서식": 2
|
||||
},
|
||||
"A4": {
|
||||
"값": "1. 해석 조건 · 판정 요약",
|
||||
"서식": 3
|
||||
},
|
||||
"B4": {
|
||||
"서식": 3
|
||||
},
|
||||
"C4": {
|
||||
"서식": 3
|
||||
},
|
||||
"D4": {
|
||||
"서식": 3
|
||||
},
|
||||
"E4": {
|
||||
"서식": 3
|
||||
},
|
||||
"F4": {
|
||||
"서식": 3
|
||||
},
|
||||
"G4": {
|
||||
"서식": 3
|
||||
},
|
||||
"H4": {
|
||||
"서식": 3
|
||||
},
|
||||
"A5": {
|
||||
"값": "투영 · 측방 한계",
|
||||
"서식": 4
|
||||
},
|
||||
"B5": {
|
||||
"서식": 5
|
||||
},
|
||||
"C5": {
|
||||
"서식": 5
|
||||
},
|
||||
"D5": {
|
||||
"서식": 5
|
||||
},
|
||||
"E5": {
|
||||
"서식": 5
|
||||
},
|
||||
"F5": {
|
||||
"서식": 5
|
||||
},
|
||||
"G5": {
|
||||
"서식": 5
|
||||
},
|
||||
"H5": {
|
||||
"서식": 5
|
||||
},
|
||||
"A6": {
|
||||
"값": "암 단위중량",
|
||||
"서식": 4
|
||||
},
|
||||
"B6": {
|
||||
"서식": 5
|
||||
},
|
||||
"C6": {
|
||||
"서식": 5
|
||||
},
|
||||
"D6": {
|
||||
"서식": 5
|
||||
},
|
||||
"E6": {
|
||||
"서식": 5
|
||||
},
|
||||
"F6": {
|
||||
"서식": 5
|
||||
},
|
||||
"G6": {
|
||||
"서식": 5
|
||||
},
|
||||
"H6": {
|
||||
"서식": 5
|
||||
},
|
||||
"A7": {
|
||||
"값": "우기 지하수",
|
||||
"서식": 4
|
||||
},
|
||||
"B7": {
|
||||
"서식": 5
|
||||
},
|
||||
"C7": {
|
||||
"서식": 5
|
||||
},
|
||||
"D7": {
|
||||
"서식": 5
|
||||
},
|
||||
"E7": {
|
||||
"서식": 5
|
||||
},
|
||||
"F7": {
|
||||
"서식": 5
|
||||
},
|
||||
"G7": {
|
||||
"서식": 5
|
||||
},
|
||||
"H7": {
|
||||
"서식": 5
|
||||
},
|
||||
"A8": {
|
||||
"값": "인장균열 · 볼트",
|
||||
"서식": 4
|
||||
},
|
||||
"B8": {
|
||||
"서식": 5
|
||||
},
|
||||
"C8": {
|
||||
"서식": 5
|
||||
},
|
||||
"D8": {
|
||||
"서식": 5
|
||||
},
|
||||
"E8": {
|
||||
"서식": 5
|
||||
},
|
||||
"F8": {
|
||||
"서식": 5
|
||||
},
|
||||
"G8": {
|
||||
"서식": 5
|
||||
},
|
||||
"H8": {
|
||||
"서식": 5
|
||||
},
|
||||
"A9": {
|
||||
"값": "판정 요약",
|
||||
"서식": 4
|
||||
},
|
||||
"B9": {
|
||||
"서식": 5
|
||||
},
|
||||
"C9": {
|
||||
"서식": 5
|
||||
},
|
||||
"D9": {
|
||||
"서식": 5
|
||||
},
|
||||
"E9": {
|
||||
"서식": 5
|
||||
},
|
||||
"F9": {
|
||||
"서식": 5
|
||||
},
|
||||
"G9": {
|
||||
"서식": 5
|
||||
},
|
||||
"H9": {
|
||||
"서식": 5
|
||||
},
|
||||
"A11": {
|
||||
"값": "2. 절리군 (현장 조사값)",
|
||||
"서식": 3
|
||||
},
|
||||
"B11": {
|
||||
"서식": 3
|
||||
},
|
||||
"C11": {
|
||||
"서식": 3
|
||||
},
|
||||
"D11": {
|
||||
"서식": 3
|
||||
},
|
||||
"E11": {
|
||||
"서식": 3
|
||||
},
|
||||
"F11": {
|
||||
"서식": 3
|
||||
},
|
||||
"G11": {
|
||||
"서식": 3
|
||||
},
|
||||
"H11": {
|
||||
"서식": 3
|
||||
},
|
||||
"A12": {
|
||||
"값": "절리군",
|
||||
"서식": 6
|
||||
},
|
||||
"B12": {
|
||||
"값": "경사방향 (°)",
|
||||
"서식": 6
|
||||
},
|
||||
"C12": {
|
||||
"값": "경사 (°)",
|
||||
"서식": 6
|
||||
},
|
||||
"D12": {
|
||||
"값": "φj (°)",
|
||||
"서식": 6
|
||||
},
|
||||
"E12": {
|
||||
"값": "cj (kPa)",
|
||||
"서식": 6
|
||||
},
|
||||
"F12": {
|
||||
"값": "지속성 (m)",
|
||||
"서식": 6
|
||||
},
|
||||
"G12": {
|
||||
"값": "구간 시작",
|
||||
"서식": 6
|
||||
},
|
||||
"H12": {
|
||||
"값": "구간 끝",
|
||||
"서식": 6
|
||||
},
|
||||
"A13": {
|
||||
"서식": 7
|
||||
},
|
||||
"B13": {
|
||||
"서식": 16
|
||||
},
|
||||
"C13": {
|
||||
"서식": 16
|
||||
},
|
||||
"D13": {
|
||||
"서식": 16
|
||||
},
|
||||
"E13": {
|
||||
"서식": 16
|
||||
},
|
||||
"F13": {
|
||||
"서식": 13
|
||||
},
|
||||
"G13": {
|
||||
"서식": 7
|
||||
},
|
||||
"H13": {
|
||||
"서식": 7
|
||||
},
|
||||
"A15": {
|
||||
"값": "3. 측점별 판정 — 위험 모드 × 조건",
|
||||
"서식": 3
|
||||
},
|
||||
"B15": {
|
||||
"서식": 3
|
||||
},
|
||||
"C15": {
|
||||
"서식": 3
|
||||
},
|
||||
"D15": {
|
||||
"서식": 3
|
||||
},
|
||||
"E15": {
|
||||
"서식": 3
|
||||
},
|
||||
"F15": {
|
||||
"서식": 3
|
||||
},
|
||||
"G15": {
|
||||
"서식": 3
|
||||
},
|
||||
"H15": {
|
||||
"서식": 3
|
||||
},
|
||||
"A16": {
|
||||
"값": "측점",
|
||||
"서식": 6
|
||||
},
|
||||
"B16": {
|
||||
"값": "비탈 (경사 / 방향 · 높이)",
|
||||
"서식": 6
|
||||
},
|
||||
"C16": {
|
||||
"값": "파괴 형태",
|
||||
"서식": 6
|
||||
},
|
||||
"D16": {
|
||||
"값": "조건",
|
||||
"서식": 6
|
||||
},
|
||||
"E16": {
|
||||
"값": "Fs",
|
||||
"서식": 6
|
||||
},
|
||||
"F16": {
|
||||
"값": "기준",
|
||||
"서식": 6
|
||||
},
|
||||
"G16": {
|
||||
"값": "판정",
|
||||
"서식": 6
|
||||
},
|
||||
"H16": {
|
||||
"값": "비고",
|
||||
"서식": 6
|
||||
},
|
||||
"A17": {
|
||||
"서식": 7
|
||||
},
|
||||
"B17": {
|
||||
"서식": 5
|
||||
},
|
||||
"C17": {
|
||||
"서식": 7
|
||||
},
|
||||
"D17": {
|
||||
"서식": 7
|
||||
},
|
||||
"E17": {
|
||||
"서식": 8
|
||||
},
|
||||
"F17": {
|
||||
"서식": 8
|
||||
},
|
||||
"G17": {
|
||||
"서식": 7
|
||||
},
|
||||
"H17": {
|
||||
"서식": 5
|
||||
}
|
||||
},
|
||||
"병합": [
|
||||
"A1:H1",
|
||||
"A2:H2",
|
||||
"A4:H4",
|
||||
"B5:H5",
|
||||
"B6:H6",
|
||||
"B7:H7",
|
||||
"B8:H8",
|
||||
"B9:H9",
|
||||
"A11:H11",
|
||||
"A15:H15"
|
||||
],
|
||||
"열": {
|
||||
"A": {
|
||||
"폭": 14
|
||||
},
|
||||
"B": {
|
||||
"폭": 26
|
||||
},
|
||||
"C": {
|
||||
"폭": 16
|
||||
},
|
||||
"D": {
|
||||
"폭": 7
|
||||
},
|
||||
"E": {
|
||||
"폭": 8
|
||||
},
|
||||
"F": {
|
||||
"폭": 8
|
||||
},
|
||||
"G": {
|
||||
"폭": 8
|
||||
},
|
||||
"H": {
|
||||
"폭": 24
|
||||
}
|
||||
},
|
||||
"행": {
|
||||
"1": {
|
||||
"높이": 36
|
||||
},
|
||||
"2": {
|
||||
"높이": 30
|
||||
},
|
||||
"4": {
|
||||
"높이": 22
|
||||
},
|
||||
"11": {
|
||||
"높이": 22
|
||||
},
|
||||
"12": {
|
||||
"높이": 22
|
||||
},
|
||||
"15": {
|
||||
"높이": 22
|
||||
},
|
||||
"16": {
|
||||
"높이": 22
|
||||
}
|
||||
},
|
||||
"보기": {
|
||||
"눈금선": false
|
||||
},
|
||||
"인쇄": {
|
||||
"용지": "A4",
|
||||
"방향": "portrait",
|
||||
"맞춤": {
|
||||
"폭": 1,
|
||||
"높이": 0
|
||||
},
|
||||
"여백": {
|
||||
"위": 15,
|
||||
"아래": 15,
|
||||
"왼": 10,
|
||||
"오른": 10
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "s10",
|
||||
"이름": "부록_절편",
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
## 공통
|
||||
|
||||
- 몫: 사용자 몫(표지 · 개요) = 처음 만들 때만 채움 · 다시 만들 때(보고서 갱신) 안 건드림. 프로그램 몫(목차 · 입력조건 · 측점표 · 대표단면 · 3D요약 · 광역요약 · 보강검토 · 부록_절편) = 갱신 때 통째 다시 씀 · 작업본에 없는 프로그램 몫 시트는 양식 차례 자리에 끼움 · 새 서식은 작업본 서식 표 끝에 붙여 번호를 바꿈.
|
||||
- 시트 차례: 표지 · 목차 · 개요 · 입력조건 · 측점표 · 대표단면 · 3D요약 · 광역요약 · (보강검토) · 부록_절편.
|
||||
- 몫: 사용자 몫(표지 · 개요) = 처음 만들 때만 채움 · 다시 만들 때(보고서 갱신) 안 건드림. 프로그램 몫(목차 · 입력조건 · 측점표 · 대표단면 · 3D요약 · 광역요약 · 보강검토 · 암반검토 · 부록_절편) = 갱신 때 통째 다시 씀 · 작업본에 없는 프로그램 몫 시트는 양식 차례 자리에 끼움 · 새 서식은 작업본 서식 표 끝에 붙여 번호를 바꿈.
|
||||
- 시트 차례: 표지 · 목차 · 개요 · 입력조건 · 측점표 · 대표단면 · 3D요약 · 광역요약 · (보강검토) · (암반검토) · 부록_절편.
|
||||
- PDF: 표지 = 쪽 번호 · 머리말 없음 · 본문 = 머리말(왼 사업명 = 표지 `C8` · 오른 보고서 이름) · 꼬리말 「- n / N -」(목차 = 1 쪽).
|
||||
- 사용자 확정 전 표기 한 규칙 — 기본값 그대로 = 「(기본값 · 사용자 확정 전)」 · 바꿈 = 「(사용자 입력)」 · 물성 = 「문헌 추정치 · 사용자 확정 전」.
|
||||
- 서식 번호(`서식` 표): 1 title · 2 sub · 3 section · 4 label · 5 text · 6 head · 7 body(가운데 글) · 8 fs(0.00) · 9 int(0) · 10 ng(N.G 빨강 채움) · 11 ok(O.K 초록) · 12 notice · 13 num1(#,##0.0) · 14 num0(#,##0) · 15 para(위 맞춤 긴 글) · 16 prop(0.0) · 17 chain(#,##0.00) · 18 head8 · 19 num8(0.00) · 20 num8(0.0) · 21 int8 · 22 목차 내용 · 23 목차 쪽 · 24 목차 장 · 25 sum8(0.00 · 회색) · 26 표지 제목.
|
||||
@@ -117,6 +117,14 @@
|
||||
|
||||
- 6 행 · 10 행 서식을 줄마다 복사 · 저장본 = (측점 · 종류 · 비탈)마다 가장 최근 하나.
|
||||
|
||||
## s11 암반검토 (프로그램 몫 · E06 암반 해석 저장본 · 없으면 시트를 뺌)
|
||||
|
||||
| 칸 | 내용 |
|
||||
| --- | --- |
|
||||
| `B5`~`B9` (각 B:H 병합 · 서식 5) | 투영 · 측방 한계 · 암 단위중량 · 우기 지하수(KDS 11 70 05 표 4.3-3 암반 ½ 심도) · 인장균열 · 볼트 · 판정 요약(낡으면 「E06 [해석] 뒤 [보고서 갱신]」) — 기본값 그대로 = 사용자 확정 전 표기 |
|
||||
| 11 · 12 행 꼴(제목 3 · 머리 6) 다음 절리군 하나 = 한 줄(13 행 서식) | A 이름(7) · B 경사방향 · C 경사 · D φj · E cj(16) · F 지속성(13) · G · H 구간 시작 · 끝(7) · 모르면 「-」 · 없으면 「절리군 없음」 |
|
||||
| 절리 줄 다음 한 줄 띄고 15 · 16 행 꼴 · 그 아래 측점 비탈 × 위험 모드 × 조건 = 한 줄(17 행 서식) | A 측점(7) · B 비탈 글(쪽 · 경사 / 방향 · 높이)(5) · C 파괴 형태(평면 · 쐐기 · 전도 + 절리)(7) · D 조건(7) · E Fs · F 기준(8) · G 판정(7 → 10 · 11 · 전도 「검토」) · H 비고(블록 안 생김 · 미끄러짐 면 · 지속성)(5) · 위험 모드 없는 비탈 = 「없음」 한 줄 |
|
||||
|
||||
## s10 부록_절편 (프로그램 몫 · 대표 단면마다 한 쪽)
|
||||
|
||||
양식 1~5 행 = 덩이 꼴(1 제목 · 2 단위 · 3 머리 · 4 절편 줄 서식 · 5 합계 서식). 대표 단면마다 지배 조건(Fs / 기준 가장 낮음) 임계면의 절편표.
|
||||
|
||||
@@ -261,3 +261,42 @@ def test_result2d_지배_비탈_기준_대비():
|
||||
assert view.governing_row(row)["verdict_overall"] == "ok"
|
||||
bare = {**row, "cases": {"dry": {"left": {"fs": 1.4, "required": None}}}}
|
||||
assert view.governing_row(bare)["side"] is None and view.governing_row(bare)["fs_min"] == 1.22
|
||||
|
||||
|
||||
def _wet_fs(project, key: str) -> tuple[float, float]:
|
||||
_analyze()
|
||||
assert router._JOBS[PID]["error"] is None
|
||||
rows = asyncio.run(router.result2d(UUID(PID), USER))["stations"]
|
||||
fs = next(r["fs_by_condition"] for r in rows if r["key"] == key)
|
||||
return fs["dry"], fs["wet"]
|
||||
|
||||
|
||||
def test_지하수_갈래_넷이_안전율을_바꿈(project, monkeypatch):
|
||||
"""일꾼이 E02 지하수 갈래를 엔진에 넘김 — 갈래마다 우기 안전율이 다름 · KDS 기본 = 전과 같음."""
|
||||
_analyze()
|
||||
rows = asyncio.run(router.result2d(UUID(PID), USER))["stations"]
|
||||
key = next(r["key"] for r in rows if r["key"] != WALL_KEY)
|
||||
chainage = next(r["chainage_m"] for r in rows if r["key"] == key)
|
||||
base = router._ground(project)
|
||||
|
||||
def run(**water) -> tuple[float, float]:
|
||||
ground = json.loads(json.dumps(base))
|
||||
ground["groundwater"].update(water)
|
||||
_save_ground(project, ground)
|
||||
return _wet_fs(project, key)
|
||||
|
||||
# 전 = 일꾼이 모형 기본(우기 지표 포화 · 건기 없음)을 그대로 씀
|
||||
with monkeypatch.context() as patch:
|
||||
patch.setattr(worker, "station_groundwater", lambda _g, _c, model: model["groundwater"])
|
||||
legacy = run(wet_mode="kds")
|
||||
kds = run(wet_mode="kds")
|
||||
half = run(wet_mode="depth_ratio", depth_ratio=0.5)
|
||||
ru = run(wet_mode="ru", ru=0.3)
|
||||
line = run(
|
||||
wet_mode="line", lines=[{"from_m": 0, "to_m": 1e6, "points": [[-20.0, 4.0], [20.0, 4.0]]}]
|
||||
)
|
||||
rock = ground_model._face_water(base, chainage) == "rock"
|
||||
assert kds == (half if rock else legacy) # KDS 기본 = 토사 지표 포화(전과 같음) · 암반 ½
|
||||
assert half[1] > legacy[1] and line[1] > legacy[1] # 수위가 내려가면 안전율 오름
|
||||
assert ru[1] != legacy[1] and len({round(v[1], 3) for v in (legacy, half, ru, line)}) == 4
|
||||
assert legacy[0] == kds[0] == half[0] == ru[0] == line[0] # 건기는 우기 갈래와 무관
|
||||
|
||||
@@ -37,7 +37,7 @@ STYLE_KEYS = {
|
||||
PRINT_KEYS = {"용지", "방향", "배율", "맞춤", "여백", "머리행", "쪽나눔"}
|
||||
A1 = re.compile(r"^([A-Z]{1,3})(\d+)$")
|
||||
NAMES = [
|
||||
"표지", "목차", "개요", "입력조건", "측점표", "대표단면", "3D요약", "광역요약", "보강검토",
|
||||
"표지", "목차", "개요", "입력조건", "측점표", "대표단면", "3D요약", "광역요약", "보강검토", "암반검토",
|
||||
"부록_절편",
|
||||
] # fmt: skip
|
||||
|
||||
@@ -151,7 +151,7 @@ def test_양식_계약():
|
||||
book = _form()
|
||||
_contract(book)
|
||||
assert [s["이름"] for s in book["시트"]] == NAMES
|
||||
ids = ["s1", "s9", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s10"]
|
||||
ids = ["s1", "s9", "s2", "s3", "s4", "s5", "s6", "s7", "s8", "s11", "s10"]
|
||||
assert [s["id"] for s in book["시트"]] == ids
|
||||
|
||||
|
||||
@@ -174,6 +174,7 @@ def test_양식_인쇄_쪽나눔_머리행_그림():
|
||||
"3D요약": 1,
|
||||
"광역요약": 0,
|
||||
"보강검토": 0,
|
||||
"암반검토": 0,
|
||||
}
|
||||
# N.G 빨강 조건부 서식 — 측점표 판정 열 · 서식 표에 N.G 칸 서식(빌더가 인쇄 · xlsx 용으로 직접 씀)
|
||||
assert by["측점표"]["조건부서식"][0]["범위"][0]["c0"] == 9
|
||||
@@ -237,9 +238,10 @@ def test_견본_pdf_쪽수_머리행(png):
|
||||
)
|
||||
sections = [i for i, t in enumerate(texts) if "대표 단면" in t and "안전율 Fs" in t]
|
||||
assert len(sections) == 2 # 단면마다 한 쪽
|
||||
# 표지 1 + 목차 1 + 개요 1 + 입력조건 1 + 측점표 n + 대표단면 2 + 3D 1 + 광역 1 + 보강 1 + 부록 1
|
||||
# 표지 1 + 목차 1 + 개요 1 + 입력조건 1 + 측점표 n + 대표단면 2 + 3D 1 + 광역 1 + 보강 1 + 암반 1
|
||||
# + 부록 1
|
||||
# (양식 빈 판)
|
||||
assert len(pages) == 1 + 1 + 1 + 1 + len(stab) + 2 + 1 + 1 + 1 + 1
|
||||
assert len(pages) == 1 + 1 + 1 + 1 + len(stab) + 2 + 1 + 1 + 1 + 1 + 1
|
||||
assert all(float(pages[i].mediabox.width) > float(pages[i].mediabox.height) for i in stab)
|
||||
html = spreadsheet.print_htmls([{"book": book, "figures": {k: png for k in figs}}])[0]
|
||||
assert html.count("<img ") == 3 # 단면 2 + 3D 띠 1
|
||||
|
||||
@@ -443,3 +443,51 @@ def test_analyze_station(monkeypatch):
|
||||
nets = model.station_net(e, _doc()["settings"])
|
||||
assert set(nets) == {"equal_angle", "equal_area", "phi"}
|
||||
assert nets["equal_area"]["zones"]["planar"]
|
||||
|
||||
|
||||
def test_e05_rock_sheet(monkeypatch):
|
||||
"""E05 암반검토 s11 — 조건 글 · 절리군 줄 · 측점 위험 모드 × 조건 줄 · 판정 서식."""
|
||||
from E05_StabReport import E05_StabReport_Build as build
|
||||
from E05_StabReport import E05_StabReport_Rock as rock
|
||||
from E06_StabRock import E06_StabRock_Router as router
|
||||
|
||||
cross, design = _cross("left")
|
||||
monkeypatch.setattr(
|
||||
model,
|
||||
"rock_stations",
|
||||
lambda _s: [{"chainage_m": 20.0, "label": "STA", "ground_type": "ripping_rock"}],
|
||||
)
|
||||
monkeypatch.setattr(model.src, "cross_section", lambda _s, _c: {**cross, "design": design})
|
||||
ground = {
|
||||
"materials": [],
|
||||
"stations": [],
|
||||
"safety_factors": {"dry": {"cut": 1.5}, "wet": {"cut": 1.2}},
|
||||
"seismic": {"enabled": False},
|
||||
}
|
||||
doc = _doc()
|
||||
stations = model.analyze(None, doc, ground)
|
||||
value = {
|
||||
"fingerprint": "fp",
|
||||
"settings": doc["settings"],
|
||||
"sets": doc["sets"],
|
||||
"summary": router._summary(stations),
|
||||
"stations": stations,
|
||||
"stale": False,
|
||||
}
|
||||
sheet = next(s for s in build.form()["시트"] if s["id"] == "s11")
|
||||
rock.fill_rock(sheet, value, {})
|
||||
cells = sheet["칸"]
|
||||
assert "기본값 · 사용자 확정 전" in cells["B7"]["값"] and "½" in cells["B7"]["값"]
|
||||
assert cells["B9"]["값"].startswith("깎기 비탈 1")
|
||||
assert cells["A13"]["값"] == doc["sets"][0]["name"]
|
||||
rows = [r for r in range(17, 40) if cells.get(f"A{r}", {}).get("값") == "STA"]
|
||||
assert len(rows) == len(stations[0]["cases"]) + len(stations[0]["kinematic"]["toppling"])
|
||||
for r, case in zip(rows, stations[0]["cases"]):
|
||||
assert cells[f"E{r}"]["값"] == pytest.approx(case["F"])
|
||||
assert cells[f"G{r}"]["값"] == ("O.K" if case["ok"] else "N.G")
|
||||
assert cells[f"G{r}"]["서식"] == (build.ST_OK if case["ok"] else build.ST_NG)
|
||||
old = {k: v for k, v in value.items() if k != "sets"} # 옛 저장본 = 측점에 실린 절리
|
||||
sheet = next(s for s in build.form()["시트"] if s["id"] == "s11")
|
||||
rock.fill_rock(sheet, old, {})
|
||||
assert sheet["칸"]["A13"]["값"] == doc["sets"][0]["name"]
|
||||
assert sheet["칸"]["G13"]["값"] == "-" # 구간 모름
|
||||
|
||||
@@ -129,6 +129,10 @@ export const ui_locales_de = {
|
||||
D02_Lat: ["위도", "Lat"],
|
||||
D02_Pick_Point: ["지도에 놓을 지점 고르기", "Pick the point to place on the map"],
|
||||
D02_Map_Hint: ["지도를 눌러 지점을 놓습니다.", "Click the map to place a point."],
|
||||
D02_View_Only: [
|
||||
"보기 전용 — 지점은 산림청만 고칩니다.",
|
||||
"View only — only the Forest Service edits points.",
|
||||
],
|
||||
D02_Save: ["저장", "Save"],
|
||||
D02_Saved: ["지점을 저장했습니다.", "Points saved."],
|
||||
D02_Saved_Crs: ["저장 좌표계", "Saved CRS"],
|
||||
|
||||
Reference in New Issue
Block a user