"""근거 제안 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"(? 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