fix(B08): 관 줄의 막힌 사유를 둘로 가름 — 「저장하면 풀림」과 「눌러도 안 풀림」
랩탑 [저장] 뒤 실측하니 관 9개 중 6개 값이 서고(59.0m) 3개가 막힘. 그 셋에 「[저장]을 한 번 누르면 섭니다」가 떴는데 눌러도 안 풀리는 자리였음 — cross_sections 에 그 관 자리 측점이 아예 없음(관은 놓였는데 횡단이 안 만들어짐). - 측점 목록을 함께 받아 「횡단이 있는데 길이만 없음」과 「관 자리에 횡단이 없음」을 갈라 말함 - ⚠ 관 자리 판정을 아주 좁게(0.05m) — 실측에서 관 439.55 근처에 측점 440.0 만 있었고, 길이 찾기와 같은 0.5m 로 보면 「횡단이 있다」로 읽혀 거짓 안내가 떴음. 길이는 B06 이 관이 놓인 측점에만 실으므로 뜻이 다름 실측(5601e828): 값 6줄 59.0m · 막힘 3줄이 정확히 그 셋 85.05→10.0 · 258.12→11.0 · 534.49→9.0 · 804.30→9.0 · 901.24→10.0 · 984.18→10.0 ⚠ npm run build:server-calc 돌려 새 번들로 바꿨음 — 옛 번들은 이웃 측점에도 길이를 실어 같은 관을 두 번 셀 수 있었음(랩탑이 고쳐 push 한 건). 시험 4건 추가(가른 두 문구 · 목록 없으면 종전대로 · ⚠ 옆 측점은 「있음」이 아님). 시험: 701 passed · 24 skipped (B05 코리도 1건 기존 깨짐, 무관). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -34,10 +34,21 @@ FACILITY_PIPE = "pipe"
|
||||
#: 연장을 못 찾은 줄의 막힌 갈래 — 「입력하면 풀림」이 아니라 **앞 단계가 내야 하는 값**이다.
|
||||
BLOCKED_LENGTH_MISSING = "input_missing"
|
||||
|
||||
#: ⚠ **둘을 갈라 말한다** — 「[저장]을 누르면 풀리는 것」과 「눌러도 안 풀리는 것」은
|
||||
#: 사용자가 할 일이 다르다. 뒤엣것에 앞 문구를 쓰면 눌러 보고 안 되어 헤맨다
|
||||
#: (2026-09-08 실측: 관 9개 중 3개가 **횡단 행 자체가 없는** 자리였다).
|
||||
NOTE_LENGTH_MISSING = (
|
||||
"관 연장이 아직 정본에 없습니다 — 횡단설계에서 [저장]을 한 번 누르면 "
|
||||
"그 측점의 관 길이가 남고 값이 섭니다"
|
||||
)
|
||||
NOTE_SECTION_MISSING = (
|
||||
"그 측점의 횡단 자체가 없습니다 — 관은 놓였는데 횡단이 안 만들어진 자리라 "
|
||||
"[저장]으로는 안 풀립니다. 횡단설계에서 그 측점이 서야 합니다"
|
||||
)
|
||||
#: 관 자리에 횡단이 있는지 볼 때의 허용 오차. **아주 좁게** — 옆 측점을 「있다」로 세면
|
||||
#: 거짓 안내가 된다. 길이 찾기(0.5m)보다 좁은 것은 뜻이 다르기 때문이다.
|
||||
SECTION_MATCH_TOLERANCE_M = 0.05
|
||||
|
||||
NOTE_KIND_DEFAULT = "관종을 안 정해 기본값({kind})으로 섰습니다 — 정하면 공종이 갈립니다"
|
||||
NOTE_KIND_UNKNOWN = "「{kind}」은(는) 아는 관종이 아니라 공종을 못 골랐습니다"
|
||||
|
||||
@@ -79,6 +90,7 @@ def build_rows(
|
||||
pipe_points: list[dict[str, Any]],
|
||||
designs: list[dict[str, Any]],
|
||||
mapping: dict[str, Any] | None = None,
|
||||
section_chainages: list[float] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""관 줄 목록. **값이 서는 줄도, 못 서는 줄도** 함께 낸다.
|
||||
|
||||
@@ -92,6 +104,9 @@ def build_rows(
|
||||
diameter_key = str(table.get("diameter_option_key") or "pipe_diameter_mm")
|
||||
|
||||
lengths = _length_by_chainage(designs, length_key)
|
||||
# 횡단이 **있는데 길이가 없는 것**과 **관 자리에 횡단이 없는 것**을 가르기 위한 목록.
|
||||
# 안 주면 종전대로 「[저장]하면 풀림」 하나로만 말한다.
|
||||
sections = [float(x) for x in (section_chainages or [])]
|
||||
rows: list[dict[str, Any]] = []
|
||||
notes: list[str] = []
|
||||
|
||||
@@ -111,8 +126,17 @@ def build_rows(
|
||||
kind_note = NOTE_KIND_UNKNOWN.format(kind=stored_kind)
|
||||
|
||||
length = _nearest(lengths, chainage)
|
||||
# ⚠ **관이 놓인 그 측점**이 있는지를 본다 — 옆 측점이 있는 것은 소용없다.
|
||||
# 실측(2026-09-08 `5601e828`): 관 439.55 근처에 측점 440.0 만 있었고, 0.5m 로
|
||||
# 느슨히 보면 「횡단이 있다」로 읽혀 **「[저장]하면 풀린다」는 거짓 안내**가 떴다.
|
||||
# 길이는 B06 이 **관이 놓인 측점에만** 싣는다(2026-09-08 이웃 오염을 고친 뒤).
|
||||
has_section = not sections or any(
|
||||
abs(x - chainage) <= SECTION_MATCH_TOLERANCE_M for x in sections
|
||||
)
|
||||
blocked_kind = None if length else BLOCKED_LENGTH_MISSING
|
||||
blocked_reason = "" if length else NOTE_LENGTH_MISSING
|
||||
blocked_reason = ""
|
||||
if not length:
|
||||
blocked_reason = NOTE_LENGTH_MISSING if has_section else NOTE_SECTION_MISSING
|
||||
if code is None:
|
||||
blocked_kind = blocked_kind or BLOCKED_LENGTH_MISSING
|
||||
blocked_reason = blocked_reason or kind_note
|
||||
|
||||
@@ -93,6 +93,8 @@ async def get_earthwork_table(project_id: UUID, route_id: int) -> JSONResponse:
|
||||
for row in designs
|
||||
if isinstance(row, dict) and (row.get("design") or {}).get("pipe_length_m") is not None
|
||||
]
|
||||
# 횡단이 선 측점 목록 — 관 줄이 「길이가 없음」과 「횡단 자체가 없음」을 가르는 데 쓴다.
|
||||
table["section_chainages"] = [row.get("chainage_m") for row in designs if isinstance(row, dict)]
|
||||
table["haul"] = haul
|
||||
# 운반계획은 [저장]·[확정]에서 정본에 남는 값이다 — 아직 없으면 빈 표가 정직하다.
|
||||
table["haul_available"] = bool(plan)
|
||||
|
||||
@@ -164,7 +164,11 @@ async def get_handoff(project_id: UUID) -> JSONResponse:
|
||||
# 준비공·사방공 — 값이 서는 줄도, 못 내는 줄도 함께 넘긴다(빼면 빠진 줄이 안 보임).
|
||||
preparation_table=earthwork.get("preparation"),
|
||||
# 배수관 — 관 정본은 `pipe_points.json`, 연장은 측점 `design.pipe_length_m` 다.
|
||||
pipe_table=_pipe_table(project_root, earthwork.get("pipe_lengths") or []),
|
||||
pipe_table=_pipe_table(
|
||||
project_root,
|
||||
earthwork.get("pipe_lengths") or [],
|
||||
earthwork.get("section_chainages") or [],
|
||||
),
|
||||
ground_class_set=settings.get("rock_class_set"),
|
||||
ground_classes=rock_classes(settings),
|
||||
ground_methods={name: rock_method(settings, name) for name in rock_classes(settings)},
|
||||
@@ -177,7 +181,11 @@ async def get_handoff(project_id: UUID) -> JSONResponse:
|
||||
return JSONResponse(content=handoff)
|
||||
|
||||
|
||||
def _pipe_table(project_root: str, pipe_lengths: list[dict[str, Any]]) -> dict[str, Any]:
|
||||
def _pipe_table(
|
||||
project_root: str,
|
||||
pipe_lengths: list[dict[str, Any]],
|
||||
section_chainages: list[Any] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""배수관 표 — 정본 셋을 읽어 잇는다. 못 읽으면 **빈 표**(줄이 안 서는 것이 정직하다).
|
||||
|
||||
⚠ 관 정본은 `structures.json` 이 아니라 `pipe_points.json` 이다
|
||||
@@ -206,7 +214,12 @@ def _pipe_table(project_root: str, pipe_lengths: list[dict[str, Any]]) -> dict[s
|
||||
{"chainage_m": row.get("chainage_m"), "design": {"pipe_length_m": row.get("pipe_length_m")}}
|
||||
for row in pipe_lengths
|
||||
]
|
||||
return build_pipe_rows(points, designs, (load_mapping().pipe or {}))
|
||||
return build_pipe_rows(
|
||||
points,
|
||||
designs,
|
||||
(load_mapping().pipe or {}),
|
||||
[float(x) for x in (section_chainages or []) if x is not None],
|
||||
)
|
||||
|
||||
|
||||
async def _earthwork_tables(project_id: UUID) -> dict[str, Any]:
|
||||
|
||||
Reference in New Issue
Block a user