feat(B09): 264-2 공기 옵션 셈 짝 · 다듬기 · 시험
- 셈 짝(Py · TS) 새 옵션 — 착공일(그날부터 · A 날수 비례) · 기후 자료 고르기 · 쉬는 날 · 공휴일 빼기 · 투입 기본 · 반영률 기본 · 주 장비 기준(장비 시간 합) · 중복 C 끝수 · 마지막 달 비작업 - 원천 = 기후 셋(거창 25 · 울진 24 · 울진 소광 25 · 0에서 직접) · 단위당 장비 시간 합 - clean_schedule 새 칸(모르는 값 · 범위 밖 버림) - 시험 test_264_2 — 기본값 = 지금 결과 · 옵션마다 값 바뀜 · 거울 TS = Py Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01STqEqztUthVFBmEP18wBSz
This commit is contained in:
@@ -9,12 +9,14 @@
|
||||
공휴일 = `B09_Draft_Schedule_Holidays.json`(2024~2040 · 법정 · 대체 · 확정 선거일 — 공개 `holidays`
|
||||
목록 0.106 에서 뽑음 · 2027 이후 선거일은 날짜가 안 정해져 뺌 · 달마다 사용자 덮개)
|
||||
기후 기본값 = 참고 설계서(25 거창) 월별 강수일(일강수량 5mm 이상) · 혹서일(33℃ 이상) —
|
||||
지역마다 사용자 고침
|
||||
사용자 값 = user["schedule"] — 다듬기 `clean_schedule`(모르는 칸 버림 · 수 칸은 수)
|
||||
고르기(264) = 참고 설계서 셋(거창 25 · 울진 24 · 울진 소광 25 「공사기간산정표」) · 0에서 직접 ·
|
||||
달마다 사용자 덮개가 이김
|
||||
사용자 값 = user["schedule"] — 다듬기 `clean_schedule`(모르는 칸 버림 · 수 칸은 수 · 고르기 칸은 목록 안)
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import calendar
|
||||
import datetime
|
||||
import json
|
||||
from pathlib import Path
|
||||
@@ -22,7 +24,7 @@ from typing import Any
|
||||
|
||||
from B09_Estimation.B09_Estimation_Calc_Base import num
|
||||
from B09_Estimation.B09_Estimation_Summary import expand, hopyo_lines
|
||||
from B09_Estimation.draft.B09_Draft_Schedule_Calc import compute_schedule
|
||||
from B09_Estimation.draft.B09_Draft_Schedule_Calc import HOLIDAY_KINDS, compute_schedule
|
||||
|
||||
HOLIDAYS = Path(__file__).with_name("B09_Draft_Schedule_Holidays.json")
|
||||
CLIMATE = {
|
||||
@@ -30,8 +32,50 @@ CLIMATE = {
|
||||
"rain": [1.2, 1.4, 3.6, 4.5, 3.6, 4.4, 7.7, 6.9, 5.5, 3.1, 2.2, 1.2],
|
||||
"heat": [0, 0, 0, 0, 0.5, 1.2, 5.9, 8.4, 0, 0, 0, 0],
|
||||
}
|
||||
#: 기후 자료 고르기(264) — 첫 줄 = 기본 · rain_note · heat_note = 공기집계 비고
|
||||
CLIMATES = {
|
||||
"geochang25": {
|
||||
**CLIMATE,
|
||||
"name": "거창 25 참고",
|
||||
"rain_note": "일강수량 5mm 이상",
|
||||
"heat_note": "33℃ 이상",
|
||||
},
|
||||
"uljin24": {
|
||||
"name": "울진 24 참고",
|
||||
"label": "경북 울진(참고 설계서 24 · 강수 = 일강수량 5mm 이상 · 혹서 없음)",
|
||||
"rain": [0.6, 1.3, 2.9, 4.3, 3.2, 3.9, 7.4, 8.3, 4, 2.8, 2.6, 1.4],
|
||||
"heat": [0] * 12,
|
||||
"rain_note": "일강수량 5mm 이상",
|
||||
"heat_note": "혹서 없음(참고 설계서)",
|
||||
},
|
||||
"sogwang25": {
|
||||
"name": "울진 소광 25 참고",
|
||||
"label": "경북 울진 소광(참고 설계서 25 · 강수 = 일강수량 10mm 이상 · 혹서 없음)",
|
||||
"rain": [2.1, 2, 3.3, 4, 2.1, 3.7, 6.3, 7.9, 5, 4.2, 3.6, 1.6],
|
||||
"heat": [0] * 12,
|
||||
"rain_note": "일강수량 10mm 이상",
|
||||
"heat_note": "혹서 없음(참고 설계서)",
|
||||
},
|
||||
"zero": {
|
||||
"name": "0에서 직접",
|
||||
"label": "직접 입력(달마다 강수 · 혹서 칸)",
|
||||
"rain": [0] * 12,
|
||||
"heat": [0] * 12,
|
||||
"rain_note": "직접 입력",
|
||||
"heat_note": "직접 입력",
|
||||
},
|
||||
}
|
||||
START_MONTH = 3
|
||||
NUMBERS = ("hours", "prep_docs", "prep_setup", "etc", "tidy")
|
||||
NUMBERS = ("hours", "prep_docs", "prep_setup", "etc", "tidy", "rate_haul", "rate_base")
|
||||
POSITIVE = ("units", "crew")
|
||||
#: 고르기 칸 — 기본값이 첫째
|
||||
CHOICES = {
|
||||
"climate_set": tuple(CLIMATES),
|
||||
"rest": ("sun", "satsun"),
|
||||
"machine_basis": ("max", "sum"),
|
||||
"c_round": ("none", "round1"),
|
||||
"last_off": ("ratio", "all"),
|
||||
}
|
||||
|
||||
|
||||
def unit_resources(sheets: dict[str, Any]) -> dict[str, dict[str, Any]]:
|
||||
@@ -54,7 +98,13 @@ def unit_resources(sheets: dict[str, Any]) -> dict[str, dict[str, Any]]:
|
||||
name = " ".join(t for t in (src.get("name"), src.get("spec")) if t) or mkey
|
||||
machine = {"key": mkey, "name": name, "hr": hr}
|
||||
labor = sum((used.get("labor") or {}).values())
|
||||
out[row["key"]] = {"machine": machine, "labor": labor if labor > 0 else None}
|
||||
# 264 주 장비 기준 「장비 시간 합」 = 차례로 일하는 장비 · n = 장비 수
|
||||
spent = [hr for hr in hours.values() if hr > 0]
|
||||
out[row["key"]] = {
|
||||
"machine": machine,
|
||||
"labor": labor if labor > 0 else None,
|
||||
"machine_sum": {"hr": sum(spent), "n": len(spent)} if machine else None,
|
||||
}
|
||||
return out
|
||||
|
||||
|
||||
@@ -71,6 +121,7 @@ def schedule_inputs(sheets: dict[str, Any], today: datetime.date | None = None)
|
||||
"units": unit_resources(sheets),
|
||||
"holidays": holiday_table(),
|
||||
"climate": CLIMATE,
|
||||
"climates": CLIMATES,
|
||||
"start": {"year": today.year + 1, "month": START_MONTH},
|
||||
}
|
||||
|
||||
@@ -100,12 +151,25 @@ def clean_schedule(raw: Any) -> dict[str, Any]:
|
||||
out: dict[str, Any] = {}
|
||||
for field in NUMBERS:
|
||||
v = num(raw.get(field))
|
||||
if v is not None and 0 <= v <= 1000:
|
||||
if v is not None and 0 <= v <= (100 if field.startswith("rate") else 1000):
|
||||
out[field] = v
|
||||
for field in POSITIVE:
|
||||
if (v := _positive(raw.get(field))) is not None and v <= 1e4:
|
||||
out[field] = v
|
||||
for field, allowed in CHOICES.items():
|
||||
if raw.get(field) in allowed:
|
||||
out[field] = raw[field]
|
||||
off = raw.get("holiday_off") if isinstance(raw.get("holiday_off"), list) else []
|
||||
kinds = [k for k in HOLIDAY_KINDS if k in off]
|
||||
if kinds:
|
||||
out["holiday_off"] = kinds
|
||||
start = raw.get("start") if isinstance(raw.get("start"), dict) else {}
|
||||
year, month = num(start.get("year")), num(start.get("month"))
|
||||
if year and month and 2000 <= year <= 2100 and 1 <= month <= 12:
|
||||
out["start"] = {"year": int(year), "month": int(month)}
|
||||
day = num(start.get("day"))
|
||||
if day and 1 < day <= calendar.monthrange(int(year), int(month))[1]:
|
||||
out["start"]["day"] = int(day)
|
||||
climate = raw.get("climate") if isinstance(raw.get("climate"), dict) else {}
|
||||
kept = {k: v for k in ("rain", "heat") if (v := _twelve(climate.get(k)))}
|
||||
if kept:
|
||||
|
||||
Reference in New Issue
Block a user