Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BVPp2o6Mh2FZJr72yW3ofZ
147 lines
7.8 KiB
Python
147 lines
7.8 KiB
Python
"""토공 보조 탭 셋 — 토공집계표 · 평균운반거리 · 종방향 무대운반거리 (PLAN 69-2).
|
||
|
||
근거 = 거창 실무 워크북(`00.25년_산불_거창(…)내역+수량.xlsx`)의 같은 이름 시트 칸 · 식 그대로.
|
||
|
||
평균운반거리 덤프트럭 · 불도우저 각 토량(Q) · 거리(L) · Q×L · 합계 ·
|
||
평균 = ROUND(ΣQ×L ÷ ΣQ, 2)
|
||
종방향 무대운반거리 무대운반 토량(Q) · 거리(L) · Q×L · 합계 · 평균(같은 식)
|
||
토공집계표 토적표 합계 줄 + 위 두 시트 ΣQ 를 시트 식대로 묶음
|
||
|
||
Q · L 출처 = 저장된 유토곡선 배분(`mass_haul.haul_plan` — B07 토적도가 그리는 것과 같은 정본) ·
|
||
장비 띠(`blocks[].bands[]`)와 떨어진 구간 운반(`transfers[]`)의 `equipment` 로 가름
|
||
(`free_haul` = 무대 · `dozer` = 불도우저 · `dump_truck` = 덤프트럭).
|
||
|
||
암 갈래 — 「연암」 · 「경암」 줄 = 암 전체 × 비율(좌측 패널 · 기본 90 : 10 · PLAN 122).
|
||
풍화암 · 보통암 · 구조물 쪽 줄(su2 시트)은 값이 없어 비움(None).
|
||
|
||
짝 — `rock_split` · `split_rock_columns` ↔ `B08_Quantity_Options_Calc.ts` `rockSplit` ·
|
||
`splitRockColumns` · `splitRockSummary`(거울 시험 `test_122_b08_options_mirror`).
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from decimal import ROUND_HALF_UP, Decimal
|
||
from typing import Any
|
||
|
||
from common_util.common_util_project_settings import SOFT_ROCK_PCT_DEFAULT
|
||
|
||
#: 운반 시트 열 — 장비 키
|
||
HAUL_EQUIPMENT = ("dump_truck", "dozer", "free_haul")
|
||
#: 암 칸 → 연암 칸 · 경암 칸(토적표 · 합계) · 칸 꼬리 셋
|
||
ROCK_SPLIT = (
|
||
("cut_rock", "cut_soft_rock", "cut_hard_rock"),
|
||
("ditch_rock", "ditch_soft_rock", "ditch_hard_rock"),
|
||
)
|
||
ROCK_SUFFIXES = ("area_m2", "volume_m3", "adjusted_m3")
|
||
|
||
|
||
def excel_round(value: float, digits: int = 0) -> float:
|
||
"""엑셀 ROUND — 반은 0 에서 먼 쪽(파이썬 round 는 짝수 쪽)."""
|
||
step = Decimal(1).scaleb(-digits)
|
||
return float(Decimal(str(value)).quantize(step, rounding=ROUND_HALF_UP))
|
||
|
||
|
||
def rock_split(value: float, soft_pct: float) -> tuple[float, float]:
|
||
"""암 → (연암, 경암) — 연암 = 암 × 비율(0~100 자름) · 경암 = 암 − 연암."""
|
||
soft = value * min(max(soft_pct, 0.0), 100.0) / 100.0
|
||
return soft, value - soft
|
||
|
||
|
||
def split_rock_columns(record: dict[str, Any], soft_pct: float) -> dict[str, Any]:
|
||
"""줄 · 합계의 암 칸마다 연암 · 경암 칸을 더함(숫자인 칸만)."""
|
||
for base, soft_key, hard_key in ROCK_SPLIT:
|
||
for suffix in ROCK_SUFFIXES:
|
||
value = record.get(f"{base}_{suffix}")
|
||
if isinstance(value, (int, float)) and not isinstance(value, bool):
|
||
soft, hard = rock_split(float(value), soft_pct)
|
||
record[f"{soft_key}_{suffix}"] = soft
|
||
record[f"{hard_key}_{suffix}"] = hard
|
||
return record
|
||
|
||
|
||
def haul_lists(haul_plan: dict[str, Any] | None) -> dict[str, dict[str, Any]]:
|
||
"""장비별 `{rows: [{q, l, ql}], q, ql, average_m}` — 시트 한 줄 = 띠 · 운반 하나."""
|
||
items: dict[str, list[dict[str, float]]] = {key: [] for key in HAUL_EQUIPMENT}
|
||
plan = haul_plan if isinstance(haul_plan, dict) else {}
|
||
bands = [band for block in plan.get("blocks") or [] for band in block.get("bands") or []]
|
||
for item in [*bands, *(plan.get("transfers") or [])]:
|
||
rows = items.get(str(item.get("equipment")))
|
||
q, dist = item.get("volume_m3"), item.get("haul_distance_m")
|
||
if rows is None or not isinstance(q, (int, float)) or not isinstance(dist, (int, float)):
|
||
continue
|
||
rows.append({"q": float(q), "l": float(dist), "ql": float(q) * float(dist)})
|
||
out: dict[str, dict[str, Any]] = {}
|
||
for key, rows in items.items():
|
||
q = sum(row["q"] for row in rows)
|
||
ql = sum(row["ql"] for row in rows)
|
||
out[key] = {
|
||
"rows": rows,
|
||
"q": q,
|
||
"ql": ql,
|
||
"average_m": excel_round(ql / q, 2) if q > 0 else None,
|
||
}
|
||
return out
|
||
|
||
|
||
def _row(group: str, kind: str, spec: str, unit: str, value: float | None, note: str = ""):
|
||
return {"group": group, "kind": kind, "spec": spec, "unit": unit, "value": value, "note": note}
|
||
|
||
|
||
def summary_rows(
|
||
totals: dict[str, float],
|
||
haul: dict[str, dict[str, Any]],
|
||
soft_pct: float = SOFT_ROCK_PCT_DEFAULT,
|
||
) -> list[dict[str, Any]]:
|
||
"""토공집계표 줄 — 시트 C5:G37 차례 · 식 그대로(셀 주소는 줄 끝 주석) · 암 = 연암 % 로 나눔."""
|
||
t = totals
|
||
cut_soft, cut_hard = rock_split(t["cut_rock_volume_m3"], soft_pct)
|
||
ditch_soft, ditch_hard = rock_split(t["ditch_rock_volume_m3"], soft_pct)
|
||
r0 = excel_round
|
||
free_q, dump_q, dozer_q = haul["free_haul"]["q"], haul["dump_truck"]["q"], haul["dozer"]["q"]
|
||
diverted = t["diverted_m3"]
|
||
tree = r0(t["tree_removal_fill_area_m2"] * 0.5 + t["tree_removal_cut_area_m2"], 2)
|
||
m3 = "㎥"
|
||
return [
|
||
_row("흙깍기", "토사", "기계(굴삭기)", m3, r0(t["cut_soil_volume_m3"])), # 토적표!E137
|
||
_row("흙깍기", "풍화암", "굴삭기 + 브레카", m3, None),
|
||
_row("흙깍기", "연암", "굴삭기 + 브레카", m3, r0(cut_soft)),
|
||
_row("흙깍기", "보통암", "굴삭기 + 브레카", m3, None),
|
||
_row("흙깍기", "경암", "발파 + 굴삭기", m3, r0(cut_hard)),
|
||
_row("측구 터파기", "토사", "기계", m3, r0(t["ditch_soil_volume_m3"])), # 토적표!K137
|
||
_row("측구 터파기", "풍화암", "굴삭기 + 브레카", m3, None),
|
||
_row("측구 터파기", "연암", "굴삭기 + 브레카", m3, r0(ditch_soft)),
|
||
_row("측구 터파기", "보통암", "굴삭기 + 브레카", m3, None),
|
||
_row("측구 터파기", "경암", "발파 + 굴삭기", m3, r0(ditch_hard)),
|
||
_row("보정량계", "", "", m3, r0(t["adjusted_total_m3"])), # 토적표!P137
|
||
_row("성토", "", "", m3, r0(t["fill_volume_m3"])), # 토적표!R137
|
||
_row("유용토 소계", "", "", m3, diverted + free_q), # G18+G19
|
||
_row("횡방향유용토", "", "백호우", m3, diverted, "유용토"), # 토적표!S137
|
||
_row("종방향유용토", "", "백호우", m3, free_q, "유용토"), # 종방향무대운반거리!B25
|
||
_row("토사운반", "", "BH+DT+BH", m3, dump_q), # 평균운반거리!B42
|
||
_row("토사운반", "", "19 TON B/D", m3, dozer_q), # 평균운반거리 불도우저 ΣQ
|
||
_row("잔토처리", "", "백호우", m3, r0(t["balance_m3"])), # 토적표!T137
|
||
_row("층따기", "", "백호우", m3, t["benching_area_m2"]), # 토적표!W137
|
||
_row("성토면다짐", "", "", "㎡", r0(t["grading_fill_area_m2"] * 0.8), "성토면80%반영"),
|
||
_row("표토제거", "", "", "㎡", None),
|
||
_row(
|
||
"초류종자살포",
|
||
"",
|
||
"씨드스프레이",
|
||
"㎡",
|
||
r0(t["grading_fill_area_m2"] * 0.5 + t["grading_cut_area_m2"]),
|
||
"성토면50%반영+절토면",
|
||
),
|
||
_row("법면보호공", "", "", "㎡", None),
|
||
# 토적표!AI138 = ROUND(성토 × 0.5 + 절토, 2) → × 0.8
|
||
_row("지장목제거", "", "", "㎡", r0(tree * 0.8), "80%반영"),
|
||
_row("구조물터파기", "토사", "기계(굴삭기)", m3, None, "구조물 수량"),
|
||
_row("구조물터파기", "풍화암", "굴삭기 + 브레카", m3, None),
|
||
_row("구조물터파기", "연암", "굴삭기 + 브레카", m3, None, "구조물 수량"),
|
||
_row("구조물터파기", "보통암", "굴삭기 + 브레카", m3, None),
|
||
_row("구조물터파기", "경암", "발파 + 굴삭기", m3, None),
|
||
_row("토사다짐", "", "", m3, None, "구조물 수량"),
|
||
_row("되메우기", "", "", m3, None, "구조물 수량"),
|
||
_row("잔토", "", "", m3, None, "현장내 성토(유용토)"),
|
||
_row("운반성토", "", "BH+DT+BH", m3, None),
|
||
]
|