Files
Aislo/B08_Quantity/B08_Quantity_Engine_Earthwork_Summary.py
T
eomsangdonandClaude Opus 5.5 e857819d2e feat(B08): 토공 탭 — 보조 탭 넷(토공집계표 · 토적표 · 평균운반거리 · 종방향 무대운반거리) · 토적표 견본 칸 (PLAN 69-2 · 69-3)
- 토적표 칸 = 사용자 견본 32칸 · 보정량계 = 절토 보정량만 · 면 계열(면고르기 · 지장목제거 · 제근 · 노면정리) 거리 = B07 횡단 수량 함수 · 면적 = 앞뒤 평균 × 측점 거리
- 토공집계표 · 운반 시트 = 거창 실무 워크북 같은 이름 시트 칸 · 식 · Q · L = 저장된 유토곡선 배분(서버 · 읽기만)
- 시험 test_69_b08_earthwork_tabs.py · 보정량계 시험 정함대로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UoSr2sqvYtBf1dtQdus88s
2026-10-06 23:05:36 +09:00

112 lines
6.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""토공 보조 탭 셋 — 토공집계표 · 평균운반거리 · 종방향 무대운반거리 (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` = 덤프트럭).
암 갈래 — 리핑암 = 「연암」 줄 · 발파암 = 「경암」 줄. 풍화암 · 보통암 · 구조물 쪽 줄(su2 시트)은
값이 없어 비움(None).
"""
from __future__ import annotations
from decimal import ROUND_HALF_UP, Decimal
from typing import Any
#: 운반 시트 열 — 장비 키
HAUL_EQUIPMENT = ("dump_truck", "dozer", "free_haul")
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 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]]) -> list[dict[str, Any]]:
"""토공집계표 줄 — 시트 C5:G37 차례 · 식 그대로(셀 주소는 줄 끝 주석)."""
t = totals
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(t["cut_ripping_rock_volume_m3"])),
_row("흙깍기", "보통암", "굴삭기 + 브레카", m3, None),
_row("흙깍기", "경암", "발파 + 굴삭기", m3, r0(t["cut_blasting_rock_volume_m3"])),
_row("측구 터파기", "토사", "기계", m3, r0(t["ditch_soil_volume_m3"])), # 토적표!K137
_row("측구 터파기", "풍화암", "굴삭기 + 브레카", m3, None),
_row("측구 터파기", "연암", "굴삭기 + 브레카", m3, r0(t["ditch_ripping_rock_volume_m3"])),
_row("측구 터파기", "보통암", "굴삭기 + 브레카", m3, None),
_row("측구 터파기", "경암", "발파 + 굴삭기", m3, r0(t["ditch_blasting_rock_volume_m3"])),
_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),
]