- 새 판정이 아니라 구조물터파기가 쓰는 그 값(`design.ground_type` 판정) 그대로 — 그 터파기에서 나온 흙이 곧 이 잔토라 두 자리가 같은 판정을 씀 - 섞여서 못 고른 구조물은 「모름」(None)으로 보냄 — 터파기에서 다수결로 안 고른 규칙 그대로 - 한계도 그대로 넘어감(터파기 깊이가 암반 경계보다 깊은지는 안 봄) — 사유에 적음 - 받는 쪽(B06)이 사토 잔량 ea/rr/br 에 넣으면 「모르는 몫」이 사라짐 - tmp/tests 2건 추가 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
87 lines
4.1 KiB
Python
87 lines
4.1 KiB
Python
"""유토곡선이 받아야 할 **구조물 몫** — 채집석 공제와 구조물 잔토 (2026-09-08).
|
|
|
|
⚠⚠ **B08 은 내기만 하고 빼거나 더하지 않는다.** 두 값 다 **양수 ㎥** 로 주고,
|
|
공제(빼기)와 사토 가산(더하기)은 **유토곡선(B06)에서 한 번씩만** 일어난다.
|
|
부호를 넘기면 받는 쪽에서 두 번 뒤집힌다 — 실무 시트가 `−274.66` 으로 적혀 있어
|
|
실제로 겪은 자리다.
|
|
|
|
채집석 공제는 사토에서 한 번만 뺀다.
|
|
구조물 잔토는 사토에 한 번만 더한다.
|
|
|
|
⚠ **측점별로도 낸다.** 총량 하나만 주면 받는 쪽이 잔량 크기에 비례해 나눌 수밖에 없고,
|
|
그러면 **운반거리가 틀어진다**(한 곳에 몰리면 거리가 어긋남 — 공제 때 이미 짚은 자리).
|
|
구조물은 구간(start~end)이라 **그 가운데 측점**을 자리로 본다.
|
|
|
|
⚠ **지반 갈래도 함께 낸다** — 새 판정이 아니라 **구조물터파기가 이미 쓰는 그 값**이다
|
|
(`design.ground_type` 에서 뽑아 「암절취 · 심도 2~3m」로 세운 그것). 그 터파기에서 나온
|
|
흙이 곧 이 잔토이므로 **같은 판정을 두 곳이 그대로 쓴다** — 두 벌로 짜면 갈린다.
|
|
⚠ **섞여서 못 고른 구조물은 「모름」(`None`)으로 보낸다** — 터파기에서 다수결로 안 고른
|
|
그 규칙 그대로다. 받는 쪽이 그 몫을 「모르는 몫」으로 드러내면 된다.
|
|
⚠ **한계도 그대로 넘어간다** — 「터파기 깊이가 암반 경계선보다 깊은지」는 보지 않는다.
|
|
터파기 토질에 이미 있는 한계이고 여기서 새로 생기는 것이 아니다.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from B08_Quantity.B08_Quantity_Engine_UnitQuantity import COLLECTED_STONE_KEY, GROUND_TYPE_LABEL
|
|
|
|
#: 유토곡선이 읽는 칸 이름 — 양쪽이 같은 낱말을 써야 인계에서 어긋나지 않는다.
|
|
STRUCTURE_SPOIL_KEY = "structure_spoil_m3"
|
|
STRUCTURE_SPOIL_POINTS_KEY = "structure_spoil_points"
|
|
|
|
SPOIL_COMPONENT = "잔토처리"
|
|
|
|
|
|
def _center(structure: dict[str, Any]) -> float | None:
|
|
start, end = structure.get("start_m"), structure.get("end_m")
|
|
if start is None and end is None:
|
|
return None
|
|
values = [float(v) for v in (start, end) if v is not None]
|
|
return sum(values) / len(values)
|
|
|
|
|
|
def haul_inputs(unit_quantity_table: dict[str, Any] | None) -> dict[str, Any]:
|
|
"""(채집석 공제, 구조물 잔토, 측점별 잔토). 값이 없으면 `None` — 0 으로 눅이지 않는다.
|
|
|
|
⚠ `None` 과 `0.0` 은 다르다. 「아직 안 옴」과 「없음」을 받는 쪽이 갈라 봐야 한다.
|
|
"""
|
|
if not unit_quantity_table:
|
|
return {
|
|
COLLECTED_STONE_KEY: None,
|
|
STRUCTURE_SPOIL_KEY: None,
|
|
STRUCTURE_SPOIL_POINTS_KEY: [],
|
|
}
|
|
total = 0.0
|
|
points: list[dict[str, Any]] = []
|
|
for structure in unit_quantity_table.get("structures") or []:
|
|
amount = 0.0
|
|
for component in structure.get("components") or []:
|
|
if str(component.get("name") or "") != SPOIL_COMPONENT:
|
|
continue
|
|
amount += float(component.get("amount") or 0.0)
|
|
if amount <= 0:
|
|
continue
|
|
total += amount
|
|
chainage = _center(structure)
|
|
if chainage is None:
|
|
continue
|
|
# 지반 갈래 — 구조물터파기가 쓰는 그 판정을 그대로 싣는다(두 벌로 안 짠다).
|
|
ground = structure.get("ground_type") or None
|
|
points.append(
|
|
{
|
|
"chainage_m": chainage,
|
|
"spoil_m3": round(amount, 3),
|
|
"ground_type": ground,
|
|
"ground_label": GROUND_TYPE_LABEL.get(str(ground)) if ground else None,
|
|
"ground_basis": str(structure.get("ground_type_basis") or ""),
|
|
}
|
|
)
|
|
collected = unit_quantity_table.get(COLLECTED_STONE_KEY)
|
|
return {
|
|
COLLECTED_STONE_KEY: collected,
|
|
STRUCTURE_SPOIL_KEY: round(total, 3) if points or total else None,
|
|
STRUCTURE_SPOIL_POINTS_KEY: sorted(points, key=lambda row: row["chainage_m"]),
|
|
}
|