From d14cd46f51d264bd13699f02065f4585d0ecc911 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Tue, 8 Sep 2026 10:26:03 +0900 Subject: [PATCH] =?UTF-8?q?feat(=EC=88=98=EB=9F=89):=20=EA=B5=AC=EC=A1=B0?= =?UTF-8?q?=EB=AC=BC=20=EC=97=B0=EC=9E=A5=EC=97=90=20**=EA=B2=B9=EC=B9=A8?= =?UTF-8?q?=EC=9D=84=20=EC=A7=80=EC=9A=B4=20=EA=B5=AC=EA=B0=84=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D**=EC=9D=84=20=ED=95=A8=EA=BB=98=20=EB=83=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit B08 창 요청(2026-09-08) — 연장(m)만 내면 산출근거에 **「어디부터 어디까지」**를 못 적음. 실무 내역은 「산마루측구 40m」 한 줄이라 없어도 서지만, 있으면 검산이 쉬워짐. `_merge` 가 길이 대신 **합친 구간 목록**을 돌려주게 하고, 행에 `spans` (`[{start_m, end_m}, …]`)를 실음. **길이는 그 목록의 합**이라 둘이 갈릴 수 없음. ⚠ 겹침은 **합쳐서** 냄 — 데스크탑 실측: 산마루측구 둘(80~120 · 100~140)이 **60.0m** 로 합쳐져 내역서에 감(단순 합 80.0m). 구조물별로 셌으면 **20m 를 더 셀** 자리였음. 그 규칙이 이 함수 한 벌에만 있어야 하는 까닭이기도 함(측구 제외·관 소관 제외도 같음). 시험 5건 — 안 겹치면 그대로 · 겹치면 합침(그 실측 그대로) · 맞닿은 두 구간은 하나 · 품은 구간은 사라짐 · **행의 길이가 구간 합과 같은지**. 전체 513 passed · 17 skipped. Co-Authored-By: Claude Opus 5 (1M context) --- common_util/common_util_structure_lengths.py | 23 ++++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/common_util/common_util_structure_lengths.py b/common_util/common_util_structure_lengths.py index 43c617f8..bb6e45b7 100644 --- a/common_util/common_util_structure_lengths.py +++ b/common_util/common_util_structure_lengths.py @@ -23,14 +23,17 @@ from B05_Profile.B05_Profile_Structures_Schema import structure_type_map PENDING_TYPE_IDS: frozenset[str] = frozenset() -def _merge(spans: list[tuple[float, float]]) -> float: - """겹치는 구간을 합쳐 실제 덮인 길이를 낸다. +def _merge(spans: list[tuple[float, float]]) -> list[tuple[float, float]]: + """겹치는 구간을 **합쳐** 실제 덮인 구간 목록을 낸다. 같은 시설을 겹치게 두 번 넣으면 단순 합은 그 구간을 **두 번 센다**. 연장은 「덮인 길이」라 겹침을 지우는 쪽이 맞다. 원래 합(`raw_length_m`)도 함께 내보내므로 입력이 겹쳤다는 사실은 숨지 않는다. + + 합친 **구간 자체**를 돌려준다 — 길이만 내면 산출근거에 「어디부터 어디까지」를 못 적는다 + (2026-09-08 B08 창 요청). 길이는 부르는 쪽이 이 목록에서 더한다. """ - total = 0.0 + merged: list[tuple[float, float]] = [] current_start: float | None = None current_end = 0.0 for start, end in sorted(spans): @@ -40,11 +43,11 @@ def _merge(spans: list[tuple[float, float]]) -> float: if start <= current_end: current_end = max(current_end, end) continue - total += current_end - current_start + merged.append((current_start, current_end)) current_start, current_end = start, end if current_start is not None: - total += current_end - current_start - return total + merged.append((current_start, current_end)) + return merged def structure_lengths(project_root: str | Path) -> list[dict[str, Any]]: @@ -78,6 +81,7 @@ def structure_lengths(project_root: str | Path) -> list[dict[str, Any]]: rows: list[dict[str, Any]] = [] for type_id, count in counts.items(): entries = spans[type_id] + merged = _merge(entries) rows.append( { "type_id": type_id, @@ -85,9 +89,14 @@ def structure_lengths(project_root: str | Path) -> list[dict[str, Any]]: "name": types[type_id].name, "count": count, # 겹침을 지운 실제 연장 — 수량서에 쓸 값. - "length_m": round(_merge(entries), 2), + "length_m": round(sum(end - start for start, end in merged), 2), # 입력한 구간 길이의 단순 합 — 위와 다르면 구간이 겹쳐 있다는 뜻. "raw_length_m": round(sum(end - start for start, end in entries), 2), + # 겹침을 지운 **구간 목록**(누가거리 m) — 산출근거에 「어디부터 어디까지」를 + # 적는 자리다. 길이는 이 목록의 합과 같다(2026-09-08 B08 창 요청). + "spans": [ + {"start_m": round(start, 2), "end_m": round(end, 2)} for start, end in merged + ], } ) rows.sort(key=lambda row: (row["group"], row["name"]))