refactor(776-3): 지문 형식 모름 — 단위 줄 투영 한 벌 · 몫 한 벌 · 블록 단가 = 항목 · clause 대체 몸 · 오타 정규식 걷음 · 맞춤 캐시 = HP 지문 칸

- Finger — 풀이 두 벌(_text · _stmate) 걷고 블록 단위 줄 투영 하나(_units) · 글은 조건 · 옛 판 · 계수 · 「…의 N %」 만 · 셈 밖 줄 안 씀
- Shares — 몫 = 단위 줄을 절(제목 · 윗절)로 묶음 한 벌 · 식 다시 풀기(qty_of · sections_stm) 걷음 · trade = ReadEst.trade
- Elem.prices = 단위 줄 단가(글 다시 안 풂 · machines 갈래 안 건드림)
- Match.clause = Basis.clause 만 · 대체 몸 · _TYPO_CLAUSE 걷음(오타는 읽기 ReadEst.sources) · fix_typo = 사본(Line 이 부름)
- Fit.cache_path — HP 지문 칸(올린 엑셀 sha256 + 판) · 파일 그대로면 다시 안 읽음(lru) · 붙여넣기 HP 만 파일 해시
- 지문 전후 = 셈 단위 275 중 150 같음 · 다름 = 장비 절 · 글(쓰는 곳 없음) 100 · 고침(STmate 기계 두 묶음 · 거창 자재 · 끝 점 직종 · 옛 몫 금액 ÷ 단가 엉터리 · 제외금액 줄) — 골든 1위 줄마다 · 불리는 블록 206 갈래 · 1위 · 상태 전후 같음(162/177 · 기초 80 · SK 37 · 대기 89)
- S1 다시 굳힘 = 봉화 · 울진대흥 · 울진소광 자원(같은 코드 두 묶음 따로) · S2 다시 굳힘 125/275(위 까닭)
- 시험 고침 — finger(블록 = ReadEst.items · STmate 단위 줄 · 셈 밖) · fit 옛 HP(항목 · 글 없음)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-11-04 13:20:17 +09:00
co-authored by Claude Opus 5.5
parent 660e3318a9
commit 022d6375e1
8 changed files with 256 additions and 522 deletions
+5 -27
View File
@@ -343,7 +343,6 @@ def name_cands(line: dict[str, Any], got: dict[str, Any] | None = None) -> list[
# ── 인용 조항 · 이름 · 근거 차례(338 · 설계 2-4 B 차례) ─────────────────────
_OLD_TEXT = re.compile(r"(20(1\d|2[0-4])|1\d년|2[0-4]년|임도품셈|임도표준품셈|사방품셈)")
_TYPO_CLAUSE = re.compile(r"^(\d+)-(\d)(\d)(\d)$") # 13-114 → 13-11-4
_GENERIC = frozenset(
"인력 기계 장비 소계 합계 노무비 재료비 경비 적용 기준 별도계상 별도 인부 보통인부 특별인부 "
"설치 해체 운반 작업 시공 사용 포함 공구손료 경장비 손료 기계경비 재료 인건비 품셈 표준품셈 "
@@ -352,32 +351,14 @@ _GENERIC = frozenset(
def fix_typo(theirs: list) -> list[list[str]]:
"""엑셀 인용 조항 오타 — 「13-114」 → 「13-11-4」."""
out = []
for book, n in theirs or []:
m = _TYPO_CLAUSE.match(str(n))
out.append([book, f"{m.group(1)}-{m.group(2)}{m.group(3)}-{m.group(4)}" if m else n])
return out
"""엑셀 인용 조항 사본 — 오타(「13-114」 → 13-11-4)는 읽기(`ReadEst.sources`)가 이미 고침."""
return [list(x) for x in theirs or []]
def clause(theirs: list, our: dict[str, Any]) -> tuple[float, list[str], str]:
"""(점수, 맞은 우리 조항 글, 꼬리표) — 같음 1 · 윗마디 0.9(엑셀 6-2 ↔ 우리 6-2-2) · 앞 마디 0.5
·
나 창 `Basis.clause` 가 있으면 그것."""
if hasattr(hopyo_basis, "clause"):
return hopyo_basis.clause(theirs, our)
nums = [n for book, n in theirs if book == our["책"]]
same = sorted(n for n in nums if n in our["조항"])
if same:
return 1.0, [f"{our['구분']} {n}" for n in same], "조항 같음"
kids = sorted(c for c in our["조항"] for n in nums if c.startswith(n + "-"))
if kids:
return 0.9, [f"{our['구분']} {n}" for n in kids], "조항 같음"
heads = {n.rsplit("-", 1)[0] for n in nums}
near_ = sorted(n for n in our["조항"] if n.rsplit("-", 1)[0] in heads)
if near_:
return 0.5, [f"{our['구분']} {n}" for n in near_], "앞 마디 같음"
return 0.0, [], ""
"""(점수, 맞은 우리 조항 글, 꼬리표) — `M02_Hopyo_Basis.clause`(같음 1 · 윗마디 0.9 ·
앞 마디 0.5)."""
return hopyo_basis.clause(theirs, our)
def _strong_basis(line: dict[str, Any], cand: dict[str, Any]) -> bool:
@@ -393,9 +374,6 @@ def b_order(line: dict[str, Any], got: dict[str, Any] | None = None, text: Any =
근거: {로직: 근거}}` — 이름 후보 + 근거 후보 합침(접힘 없음) · 「같음」도 근거 검사(인용 조항이
다른 로직을 가리키면 후보) · 옛 판 글이면 같음 그대로 · 씨앗은 이름 같음 자리."""
got = got or targets()
basis_ = dict(line.get("근거") or {})
if basis_.get("출처"):
line = {**line, "근거": {**basis_, "출처": fix_typo(basis_["출처"])}}
basis = hopyo_basis.candidates(line, got)
named = candidates(line, "로직", got, None)
merged_c = [c for c in hopyo_basis.merge(basis, named) if c.get("로직")]