Merge remote-tracking branch 'origin/sub_desktop_1' into sub_laptop_1

This commit is contained in:
2026-09-08 05:54:31 +09:00
5 changed files with 26 additions and 2316 deletions
@@ -36,7 +36,7 @@ from decimal import Decimal
from typing import Any
from B09_Estimation.B09_Estimation_MachineCost import load_machine_catalog
from B09_Estimation.B09_Estimation_ResourceAxis import RANGE_DASHES
from B09_Estimation.B09_Estimation_ResourceAxis import RANGE_DASH_CLASS
_ZERO = Decimal(0)
_SECONDS_PER_HOUR = Decimal(3600)
@@ -65,7 +65,7 @@ _RE_PARENS = re.compile(r"[(]([^)]*)[)]")
_RE_NUMBER = re.compile(r"-?\d+(?:\.\d+)?")
_RE_FRACTION = re.compile(r"^(\d+(?:\.\d+)?)\s*/\s*(\d+(?:\.\d+)?)$")
#: 범위 표기 — 「0.550.45」·「0.20.8」. **확정값이 아니다.**
_RE_RANGE = re.compile(rf"\d+(?:\.\d+)?\s*[{RANGE_DASHES}]\s*\d+(?:\.\d+)?")
_RE_RANGE = re.compile(rf"\d+(?:\.\d+)?\s*[{RANGE_DASH_CLASS}]\s*\d+(?:\.\d+)?")
class ProductivityError(ValueError):
+20 -1
View File
@@ -151,7 +151,17 @@ class ResourceCatalog:
#: 갈래 키 정규화(`B09_Estimation_UnitPrice.normalize_variant_key`)도 이 목록을 쓴다.
RANGE_DASHES = "∼~〜~-–‐"
_RE_RANGE_CELL = re.compile(rf"^\d+(?:\.\d+)?\s*[{RANGE_DASHES}]\s*\d+(?:\.\d+)?$")
#: ⚠ **문자클래스에 그대로 넣지 말 것** — `~-–` 이 **범위 연산자**로 읽혀 거의 모든
#: 글자가 걸린다(2026-09-08 실측: 「0.7㎥」·「15톤」이 구간으로 잡혔음). 반드시 이 쪽을 쓴다.
RANGE_DASH_CLASS = "".join(re.escape(ch) for ch in RANGE_DASHES)
#: 구간 셀에는 **단위 꼬리**가 붙기도 한다 — 「51~100m」·「1214㎝」(2026-09-08 실측 39줄).
#: ⚠ **단위가 붙었다고 다 구간이 아니다** — 「굴착기 0.7㎥」는 규격이고 자원 이름의 일부다.
#: 그래서 **수 ~ 수 + 단위**라는 모양 전체가 맞을 때만 구간으로 본다(앞에 이름이 없어야 한다).
_RANGE_UNITS = "a-zA-Z㎝㎜㎥㎡㎞mm톤"
_RE_RANGE_CELL = re.compile(
rf"^\d+(?:\.\d+)?\s*[{RANGE_DASH_CLASS}]\s*\d+(?:\.\d+)?\s*[{_RANGE_UNITS}]+$"
)
_RE_HANGUL = re.compile(r"[가-힣]")
@@ -347,6 +357,15 @@ def match_table(
result: AxisResult,
) -> None:
"""표 하나에 자원 축을 붙인다. 값이 안 서면 `unmatched` 로 보낸다."""
# ⚠ **예시 서식 표는 자원 표가 아니다.** 품셈이 「ha당 …단가산출서(예시)」처럼
# **빈 서식**을 실어 두는데(4-2 · 3-2 등), 그것을 읽으려 들면 구간 라벨·항목명이
# 자원 이름으로 오해돼 못 맞춘 목록이 못 쓰게 된다(2026-09-08 실측 39줄).
# 값이 있는데 안 서는 자리가 아니라 **애초에 값이 없는 표**다.
headers = " ".join(str(c) for c in (table.get("condition_note") or []))
if "단가산출서" in headers or "예시" in headers:
result.skipped_forms["example_form"] = result.skipped_forms.get("example_form", 0) + 1
return
from B09_Estimation.B09_Estimation_CrewOutput import match_crew_table
# ⚠ **작업조 표는 형태 판정보다 먼저 가른다.** 「형틀목공 4인 / 시공량 35㎡」 표는
@@ -24,7 +24,7 @@ from decimal import Decimal
from typing import Any
from B09_Estimation.B09_Estimation_ResourceAxis import (
RANGE_DASHES,
RANGE_DASH_CLASS,
AxisResult,
ResourceCatalog,
ResourceRow,
@@ -254,7 +254,7 @@ def _resolve_packed(catalog: ResourceCatalog, name: str, specs: list[str] | None
_TRACK_PREFERENCE = ("무한궤도",)
_RE_SPEC_RANGE = re.compile(rf"^(\d+(?:\.\d+)?)[{RANGE_DASHES}](\d+(?:\.\d+)?)$")
_RE_SPEC_RANGE = re.compile(rf"^(\d+(?:\.\d+)?)[{RANGE_DASH_CLASS}](\d+(?:\.\d+)?)$")
def _spec_matches(catalog_spec: str, wanted: str) -> bool:
@@ -6293,9 +6293,10 @@
"rows": 418,
"skipped_forms": {
"coefficient": 19,
"example_form": 8,
"reference": 56,
"undetermined": 75
},
"unmatched": 882
"unmatched": 497
}
}
File diff suppressed because it is too large Load Diff