From 1ad40fa2fbe9ee2bd50bd3aaa0300399688d77f2 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Tue, 8 Sep 2026 09:40:51 +0900 Subject: [PATCH] =?UTF-8?q?fix(B08):=20=EA=B4=80=20=EC=A4=84=EC=9D=98=20?= =?UTF-8?q?=EB=A7=89=ED=9E=8C=20=EC=82=AC=EC=9C=A0=EB=A5=BC=20=EB=91=98?= =?UTF-8?q?=EB=A1=9C=20=EA=B0=80=EB=A6=84=20=E2=80=94=20=E3=80=8C=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=ED=95=98=EB=A9=B4=20=ED=92=80=EB=A6=BC=E3=80=8D?= =?UTF-8?q?=EA=B3=BC=20=E3=80=8C=EB=88=8C=EB=9F=AC=EB=8F=84=20=EC=95=88=20?= =?UTF-8?q?=ED=92=80=EB=A6=BC=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 랩탑 [저장] 뒤 실측하니 관 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) --- B08_Quantity/B08_Quantity_Engine_Pipe.py | 26 ++++++++++++++++++- B08_Quantity/B08_Quantity_Router_Earthwork.py | 2 ++ B08_Quantity/B08_Quantity_Router_Material.py | 19 +++++++++++--- 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/B08_Quantity/B08_Quantity_Engine_Pipe.py b/B08_Quantity/B08_Quantity_Engine_Pipe.py index da11bc31..9261086c 100644 --- a/B08_Quantity/B08_Quantity_Engine_Pipe.py +++ b/B08_Quantity/B08_Quantity_Engine_Pipe.py @@ -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 diff --git a/B08_Quantity/B08_Quantity_Router_Earthwork.py b/B08_Quantity/B08_Quantity_Router_Earthwork.py index fea10f5f..fc4aeaff 100644 --- a/B08_Quantity/B08_Quantity_Router_Earthwork.py +++ b/B08_Quantity/B08_Quantity_Router_Earthwork.py @@ -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) diff --git a/B08_Quantity/B08_Quantity_Router_Material.py b/B08_Quantity/B08_Quantity_Router_Material.py index 432e044c..ddada6c9 100644 --- a/B08_Quantity/B08_Quantity_Router_Material.py +++ b/B08_Quantity/B08_Quantity_Router_Material.py @@ -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]: