- 새 M02_San_Basis_Map — 옛 번호 대조표(건설 옛 11-3 → 8-2-3 · 3-7 → 3-9-1 · 8-2-35 → 8-2-32 · 근거 = 원문 표 값 · 실무 글) · 사방(옛) = 판 대응표 2018 통합 판 · 기계 상수 줄 글 읽기(여러 대입 한 줄 · 괄호 설명 · 끝값 없는 나눗셈) · 작업량당 기계경비 로직 낱말 표 - 엔진 — 같은 말(휠라 = 채움재 · 평떼 = 평잔디 · 육샹 = 육상) · 건설(옛) 같은 번호는 같은 절 + 제목 뜻 겹침만 · Q 식 절 기호 채움(고르는 차례는 채우기 전) · 값 · 제목 길이 품 검산으로 안 받쳐지면 그 기계 작업량당 기계경비(차례 「기계」 · 번호 길은 그대로) - 검산 probe — 고르기가 범위뿐이면 용량 · 규격 값이 드는 범위 하나로(버킷용량 → 규격구분) - 459 블록: 없음 85 → 58 · 확정 125 → 164 · 근접 135 → 145 · 사람 114 → 92 · 확정 · 근접 → 없음 0 · 섞기(GF000132) 오인 넷 → GC001148 · GC001189 - 시험 test_793_3a 넓힘 — 없음 ≤ 58 · 확정 ≥ 164 · 손 확인 견본 열 · 1/3 적용 지어 넣지 않음 · 상수 읽기 · 대조표 - 근거후보 꼴 그대로 · M01 · 지식DB 고침 없음 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
122 lines
5.3 KiB
Python
122 lines
5.3 KiB
Python
"""근거 제안 5 — 옛 번호 대조표 · 기계 작업량 읽기(PLAN 794-2c · 요구 653 · 분석 652-794-2a 3 장) ·
|
||
읽기만 · 지어 넣는 값 없음.
|
||
|
||
옛 번호 → 현행 절 = 대조표 `OLD`(근거 = 원문 표 값 · 실무 글) › 사방품셈(옛) = 지식DB 판 대응표
|
||
「2018 통합 산림사업 표준품셈」 확정 한 대응 › 건설품셈(옛) 같은 번호 = 현행 절 로직 이름과 블록
|
||
제목(같은 말 묶음 뒤)이 뜻 낱말을 나눌 때만(엔진 몫).
|
||
기계 작업량 절 = 글에 「3600 … Cm」 Q 식 · 기호(q · K · f · E · Cm) 끝값을 줄 글에서 읽음(여러 대입
|
||
한 줄 · 「q (버킷용량) = 0.7」 꼴) · 기계 낱말 → 그 기계 「작업량당 기계경비」 로직(이름에서 뽑음).
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import csv
|
||
import re
|
||
from functools import lru_cache
|
||
from typing import Any
|
||
|
||
from M02_MasterTemplete import M02_Hopyo_Clause as clause
|
||
|
||
# (옛 책, 옛 번호) → (현행 구분, 현행 번호, 근거)
|
||
OLD = {
|
||
("건설품셈(옛)", "11-3"): (
|
||
"건설품셈 공통",
|
||
"8-2-3",
|
||
"실무 글 「건설표준품셈 11-3(굴삭기)」(봉화 · 울진대흥 2024) = 현행 8-2-3 굴착기",
|
||
),
|
||
("건설품셈(옛)", "3-7"): (
|
||
"건설품셈 공통",
|
||
"3-9-1",
|
||
"현행 3-9-1 벌목 표(5m미만 벌목부 2.14 · 보통인부 0.51 · 굴착기+집게 2.71) = 블록 글"
|
||
" 「건설표준품셈 3-7」(동부청영월 2024)",
|
||
),
|
||
("건설품셈(옛)", "8-2-35"): (
|
||
"건설품셈 공통",
|
||
"8-2-32",
|
||
"현행 8-2-32 이동식 임목파쇄기 93.25㎾ Q 6.0 · 발전기 250㎾ · 보통인부 2인 · 굴착기 0.7㎥"
|
||
"+집게 = 블록 글 「표준품셈 8-2-35」(동부청영월 2024)",
|
||
),
|
||
}
|
||
_SABANG = "2018 통합 산림사업 표준품셈"
|
||
|
||
|
||
@lru_cache(maxsize=1)
|
||
def sabang_map() -> dict[str, str]:
|
||
"""사방품셈(옛) 번호 → 현행 산림 번호 — 판 대응표 「2018 통합」 판 · 확정 · 한 대응만."""
|
||
with open(clause._ROAD_MAP, encoding="utf-8-sig", newline="") as f:
|
||
rows = list(csv.DictReader(f))
|
||
out = {}
|
||
for row in rows:
|
||
old, new = clause._OLD_NO.match(row["과거_절"].strip()), row["현행_절"].strip()
|
||
if row["STmate_인용판"] != _SABANG or row["판정"] != "확정" or not old:
|
||
continue
|
||
if clause._OLD_NO.fullmatch(new) and " " not in new:
|
||
out[old.group(1)] = new
|
||
return out
|
||
|
||
|
||
def old_rows(book: str, number: str) -> list[dict]:
|
||
"""옛 출처 하나 → 현행 로직 줄(`clause.lookup` 꼴) · 대조 없음 = []."""
|
||
if (book, number) in OLD:
|
||
return clause.lookup(*OLD[(book, number)][:2])
|
||
if book == "사방품셈" + clause.OLD and number in sabang_map():
|
||
return clause.lookup("산림품셈", sabang_map()[number])
|
||
return []
|
||
|
||
|
||
# ── 기계 작업량 절 ──────────────────────────────────────────────────
|
||
_Q = re.compile(r"3,?600\.?\s*[×x*]")
|
||
_SYM = re.compile(
|
||
r"(?<![A-Za-z가-힣/*×·])(q|K|k|f|F|E|Cm|CM)\d?\s*(?:\((?:[^()=]|\([^()]*\))*\))?\s*="
|
||
)
|
||
_NUM = re.compile(r"\d[\d,]*(?:\.\d*)?")
|
||
_RATIO = re.compile(r"\s*\d[\d.]*\s*/\s*\d[\d.]*\s*,?\s*")
|
||
_CANON = {"k": "K", "F": "f", "CM": "Cm"}
|
||
|
||
|
||
def has_q(lines: list[str]) -> bool:
|
||
"""Q = 3600 × q × K × f × E / Cm 식 줄이 있나."""
|
||
text = " ".join(lines)
|
||
return bool(_Q.search(text)) and "Cm" in text
|
||
|
||
|
||
def consts(lines: list[str]) -> list[dict[str, Any]]:
|
||
"""줄 글 → [{기호, 값, 덧?}] — 기호 마디마다 괄호 글 뺀 마지막 수(「f = 1/1.3 = 0.77」 = 0.77)
|
||
· Cm 괄호 각도 = 덧 · 첫 값만(같은 기호 둘째부터 뺌)."""
|
||
out, seen = [], set()
|
||
for line in lines:
|
||
hits = list(_SYM.finditer(line))
|
||
for i, m in enumerate(hits):
|
||
body = line[m.end() : hits[i + 1].start() if i + 1 < len(hits) else len(line)]
|
||
plain = re.sub(r"\((?:[^()]|\([^()]*\))*\)", " ", body)
|
||
nums = [float(x.replace(",", "")) for x in _NUM.findall(plain.split("sec")[0])]
|
||
sym = _CANON.get(m.group(1), m.group(1))
|
||
if not nums or sym in seen:
|
||
continue
|
||
ratio = _RATIO.fullmatch(plain) # 「f = 1/1.25」 끝값 없는 나눗셈
|
||
one: dict[str, Any] = {
|
||
"기호": sym,
|
||
"값": round(nums[0] / nums[1], 6) if ratio else nums[-1],
|
||
}
|
||
if sym == "Cm" and (deg := re.search(r"\(\s*(\d+)\s*˚?\s*\)", body)):
|
||
one["덧"] = f"{deg.group(1)}˚"
|
||
seen.add(sym)
|
||
out.append(one)
|
||
return out
|
||
|
||
|
||
def machines(logic: dict[str, dict]) -> dict[str, str]:
|
||
"""{기계 낱말: 「작업량당 기계경비」 로직 키} — 로직 이름 첫 낱말(괄호 앞)."""
|
||
out = {}
|
||
for k, r in sorted(logic.items()):
|
||
name = str(r.get("이름") or "")
|
||
if "작업량당 기계경비" in name:
|
||
out.setdefault(re.split(r"[\s(]", name)[0], k)
|
||
return out
|
||
|
||
|
||
def machine_key(table: dict[str, str], text: str) -> str | None:
|
||
"""글(같은 말 묶음 뒤)에 기계 낱말이 하나뿐이면 그 로직 키."""
|
||
found = {k for w, k in table.items() if w in text}
|
||
return found.pop() if len(found) == 1 else None
|