Files
eomsangdonandClaude Opus 5.5 2101601f39 feat(794-2c): 근거 제안 엔진 후속 — 같은 말 묶음 · 옛 번호 대조표 · 기계 작업량 길 넓힘 · 섞기 오인 넷 고침
- 새 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>
2026-11-05 19:45:21 +09:00

122 lines
5.3 KiB
Python
Raw Permalink 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.
"""근거 제안 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