feat(b06,b08): 다단 추가 기슭막이 수량 — 서버 재계산이 선 다단 벽 목록(design.extra_walls)을 남기고 B08 이 줄을 세움 · 높이 안 적은 단은 미확정(④)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016VBGFXB9AbJBwXP19z75Qq
This commit is contained in:
@@ -315,10 +315,46 @@ def _pipe_diameter_m(options: dict[str, Any], defaults: dict[str, Any]) -> float
|
||||
return _num_or_zero(raw) / 1000.0 or 1.0
|
||||
|
||||
|
||||
def facility_structures(points: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
#: 다단 추가 기슭막이(④, 2026-09-14) — 횡단 설계가 정본이고 **실제로 선** 단 수·높이는 지형이 정함.
|
||||
#: 서버 Node 재계산(`B06_Section_Structure_Layouts.extraWallRows`)이 소유 측점 `design.extra_walls` 에
|
||||
#: 「선 다단 벽 목록」을 남기고 여기서 읽음(관 연장 `pipe_length_m` 과 같은 길).
|
||||
EXTRA_WALLS_KEY = "extra_walls"
|
||||
EXTRA_SIDE_LABELS = {"outlet": "유출측", "basin": "유입측"}
|
||||
#: 다단이 딸린 기준벽 — 유출측 다단은 유출 벽, 유입측(집수정 계류측) 다단은 유입 벽.
|
||||
EXTRA_SIDE_ROLES = {"outlet": "outlet", "basin": "inlet"}
|
||||
UNCONFIRMED_EXTRA_HEIGHT = (
|
||||
"다단 높이를 안 적음 — 기본 1.5m 를 지반에 맞춰 그린 {height}m 로 섰음 · 적으면 금액이 섬"
|
||||
)
|
||||
UNCONFIRMED_EXTRA_FORM = "다단 형태를 안 정해 기준벽의 기본 형태 「{form}」로 섰음"
|
||||
|
||||
|
||||
def extra_walls_from_designs(designs: list[dict[str, Any]] | None) -> dict[float, list[dict]]:
|
||||
"""측점별 「선 다단 벽 목록」 — 목록이 있는 측점만(빈 목록도 담음 — 「다단 없음」)."""
|
||||
found: dict[float, list[dict]] = {}
|
||||
for row in designs or []:
|
||||
design = row.get("design") if isinstance(row, dict) else None
|
||||
walls = design.get(EXTRA_WALLS_KEY) if isinstance(design, dict) else None
|
||||
if isinstance(walls, list):
|
||||
found[round(float(row.get("chainage_m") or 0.0), 3)] = walls
|
||||
return found
|
||||
|
||||
|
||||
def _extra_walls_at(extra_walls: dict[float, list[dict]] | None, chainage: float) -> list[dict]:
|
||||
"""관 자리의 주인 측점 목록 — 스냅 때문에 최대 0.5m 어긋남(`SECTION_MATCH_TOLERANCE_M`)."""
|
||||
if not extra_walls:
|
||||
return []
|
||||
best = min(extra_walls, key=lambda value: abs(value - chainage))
|
||||
return extra_walls[best] if abs(best - chainage) <= SECTION_MATCH_TOLERANCE_M else []
|
||||
|
||||
|
||||
def facility_structures(
|
||||
points: list[dict[str, Any]],
|
||||
extra_walls: dict[float, list[dict]] | None = None,
|
||||
) -> list[dict[str, Any]]:
|
||||
"""계곡 통과 시설(`pipe_points.json`) → 원단위 전개가 읽는 구조물 줄 (A1, 2026-09-14).
|
||||
|
||||
⚠ 관 자체는 안 냄 — `build_rows` 가 관 연장으로 셈.
|
||||
⚠ 다단 추가 기슭막이는 `extra_walls`(측점 → 선 벽 목록, `extra_walls_from_designs`)로 줄을 세움.
|
||||
⚠ 안 적힌 벽 칸은 **등록부 기본값** — 그 사실을 줄 사유로 붙이고 `unconfirmed` 로 금액 합에서 뺌
|
||||
(2026-09-14 브레인 판정 「줄은 서되 금액은 실제 값이 있을 때만」).
|
||||
⚠ 집수정·기슭막이 터파기·되메우기는 전개 성분(`destination: earthwork`)이라 토공집계로만 감.
|
||||
@@ -409,4 +445,42 @@ def facility_structures(points: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
unconfirmed=" · ".join(reasons),
|
||||
)
|
||||
rows.append(row)
|
||||
# 다단 — 기준벽의 기초·(독립이면) 제원 칸을 물려받고, 형태·높이·구간은 선 벽 목록 값.
|
||||
parents = {role: (values, filled) for role, _label, values, _notes, _w, filled in walls}
|
||||
counts = {"outlet": 0, "basin": 0}
|
||||
for tier in _extra_walls_at(extra_walls, chainage):
|
||||
side = "basin" if str(tier.get("side")) == "basin" else "outlet"
|
||||
counts[side] += 1
|
||||
parent_values, parent_filled = parents.get(
|
||||
EXTRA_SIDE_ROLES[side], next(iter(parents.values()), ({}, []))
|
||||
)
|
||||
height = _num_or_zero(tier.get("height_m"))
|
||||
form = str(tier.get("form") or parent_values.get("form") or "")
|
||||
before, after = _num_or_zero(tier.get("before_m")), _num_or_zero(tier.get("after_m"))
|
||||
reasons = []
|
||||
if not tier.get("height_set"):
|
||||
reasons.append(UNCONFIRMED_EXTRA_HEIGHT.format(height=f"{height:g}"))
|
||||
if not tier.get("form_set") and any(str(f).startswith("형태") for f in parent_filled):
|
||||
reasons.append(UNCONFIRMED_EXTRA_FORM.format(form=form))
|
||||
label = f"{EXTRA_SIDE_LABELS[side]} 다단 기슭막이 {counts[side]}단"
|
||||
row = child_row(base, "revetment", label, str(tier.get("key") or label))
|
||||
row.update(
|
||||
start_m=chainage - before,
|
||||
end_m=chainage + after,
|
||||
options={
|
||||
**(kept if legacy else {}),
|
||||
"form": form,
|
||||
"height_m": height,
|
||||
"length_m": before + after,
|
||||
"before_m": before,
|
||||
"after_m": after,
|
||||
"foundation": foundation,
|
||||
},
|
||||
notes=[
|
||||
f"ⓘ 횡단도에 선 다단 벽 — 높이 {height:g}m · 형태 {form} · 전 {before:g}/후 {after:g}m"
|
||||
],
|
||||
withheld=False,
|
||||
unconfirmed=" · ".join(reasons),
|
||||
)
|
||||
rows.append(row)
|
||||
return rows
|
||||
|
||||
@@ -44,6 +44,7 @@ def project_structure_sheets(
|
||||
project_root: str,
|
||||
section_modes: dict[float, str] | None,
|
||||
ground_types: dict[float, str] | None = None,
|
||||
designs: list[dict[str, Any]] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""구조물도 장 목록 — **원단위 탭(`material-summary`)과 같은 입력**으로 전개해 접는다.
|
||||
|
||||
@@ -60,7 +61,8 @@ def project_structure_sheets(
|
||||
from B08_Quantity.B08_Quantity_Router_Material import _collect_structures
|
||||
from common_util.common_util_project_settings import quantity_settings
|
||||
|
||||
structures, names, skipped = _collect_structures(project_root)
|
||||
# 설계를 넘겨야 다단 추가 기슭막이 장이 원단위 탭과 같이 섬(④).
|
||||
structures, names, skipped = _collect_structures(project_root, designs)
|
||||
settings = quantity_settings(project_root)
|
||||
templates = project_templates(project_root)
|
||||
unit_table = build_unit_table(
|
||||
@@ -87,11 +89,12 @@ async def _sheets_of(project_id: UUID, project_root: str) -> dict[str, Any]:
|
||||
|
||||
단면유형·지반 갈래는 원단위 탭 창구(`B08_Quantity_Router_Material`)를 그대로 씀.
|
||||
"""
|
||||
from B08_Quantity.B08_Quantity_Router_Material import _ground_types, _section_modes
|
||||
from B08_Quantity.B08_Quantity_Router_Material import _designs, _ground_types, _section_modes
|
||||
|
||||
modes = await _section_modes(project_id)
|
||||
ground = await _ground_types(project_id)
|
||||
return await asyncio.to_thread(project_structure_sheets, project_root, modes, ground)
|
||||
designs = await _designs(project_id)
|
||||
return await asyncio.to_thread(project_structure_sheets, project_root, modes, ground, designs)
|
||||
|
||||
|
||||
def _not_found() -> JSONResponse:
|
||||
|
||||
Reference in New Issue
Block a user