feat(810-4): 연결 찾기 뜻 근거 · 옵션 값 꼴 · 전수 판정 목록
- 확정 = 줄 · 금액 같음 + 뜻 근거(품셈 절 번호 같음 또는 이름 낱말 겹침) — 값만 같은 남의 기초(구조물되메우기 ↔ 숲가꾸기 · 모르타르 ↔ 기초다짐 등)는 다른 항 「이름 다름」 = 근접 - 찾은 옵션 = 목록 값 꼴 그대로(수 = 수) — SK 입력값 · M01 다시 셈이 같은 값(간벌재직경 「22」 글이면 표 줄 못 찾던 것 고침) - 전수 도우미 파일마다 열 갈래로 나눠 맡김(큰 파일 한 프로세스가 붙잡지 않게) · 시험 S8104_ROWS 줄 목록 저장 - 전수(M01 수식 문서 1,518 × 원파일 산근 2,220 블록) — 확정 98 · 근접 617 · 없음 1,505 · 확정 다시 셈 다름 0 · 요구 704 파형강관 Φ800 = GF000179 관경 800 근접(단위 환산 × 6 · 기계 규격 · 율 줄) · 목록 docs/raw/요구_1104/_목록_810-4_연결판정.xlsx Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -472,6 +472,7 @@ def _totals(got: dict) -> dict:
|
||||
|
||||
|
||||
EXPLAINED = (
|
||||
"이름 다름",
|
||||
"단가 차이",
|
||||
"단위 환산",
|
||||
"기계 규격",
|
||||
@@ -516,19 +517,21 @@ def _calc(doc: dict, links: Any, given: dict) -> dict | None:
|
||||
def try_doc(
|
||||
base: dict, links: Any, imported: dict, got_imp: dict, mine: list[dict], regions: set[str]
|
||||
) -> dict:
|
||||
"""M01 문서 하나 — 옵션 풀이 · 가장 나은 조합 판정."""
|
||||
"""M01 문서 하나 — 옵션 풀이 · 가장 나은 조합 판정(이름 · 절 근거 없으면 「이름 다름」 = 확정 아님)."""
|
||||
want = {
|
||||
"수량": {x["수량"] for x in mine if x["수량"] is not None},
|
||||
"단가": {x["단가"] for x in mine if x["단가"] is not None},
|
||||
}
|
||||
dom, ev = domains(base, imported, regions)
|
||||
dom = narrow(base, links, dom, want, ev)
|
||||
named = [] if same_name(imported, base) else [_unnamed(imported, base)]
|
||||
best: dict | None = None
|
||||
for pick in combos(base, dom) or [{}]:
|
||||
got = _calc(base, links, _given(base, pick))
|
||||
if got is None:
|
||||
continue
|
||||
off = [_off(base, k, pick[k], ev[k]) for k in ev if k in pick and pick[k] not in ev[k]]
|
||||
off += named
|
||||
res = verdict(mine, facts(base, got), got_imp, got, off)
|
||||
rank = (res["판정"] == SURE, -len(off), res["같음"], res["짝"], -len(res["다른"]))
|
||||
if best is None or rank > best["순위"]:
|
||||
@@ -577,17 +580,43 @@ def match(imported: dict, links_imp: Any, index: Index, links: Any) -> dict[str,
|
||||
}
|
||||
|
||||
|
||||
def _shown(doc: dict, pick: dict) -> dict[str, str]:
|
||||
"""고른 옵션(기본과 다른 것) — {열쇠: 값 글}."""
|
||||
def _shown(doc: dict, pick: dict) -> dict[str, Any]:
|
||||
"""고른 옵션(기본과 다른 것) — {열쇠: 값}(수 = 수 · 선택 목록의 수 값과 같은 꼴 · SK 입력값)."""
|
||||
out = {}
|
||||
for v in doc.get("변수") or []:
|
||||
vid = md.var_id(v)
|
||||
if vid in pick and mc.plain_text(mc.number(pick[vid])) != mc.plain_text(
|
||||
mc.number((v.get("연결") or {}).get("값"))
|
||||
):
|
||||
out[str(v.get("열쇠") or vid)] = mc.plain_text(mc.number(pick[vid]))
|
||||
out[str(v.get("열쇠") or vid)] = ml._norm(mc.number(pick[vid]))
|
||||
return out
|
||||
|
||||
|
||||
_STOP = {"설치", "해체", "인력", "기계", "작업", "운반", "타설", "제작"}
|
||||
|
||||
|
||||
def _grams(texts: list[Any]) -> set[str]:
|
||||
"""이름 낱말 — 한글 두 글자 묶음 · 영문 · 수 낱말(일반 낱말 뺌)."""
|
||||
out: set[str] = set()
|
||||
for t in texts:
|
||||
t = re.sub(r"\s|및", "", str(t or ""))
|
||||
for run in re.findall(r"[가-힣]+", t):
|
||||
out |= {run[i : i + 2] for i in range(len(run) - 1)}
|
||||
out |= {w.lower() for w in re.findall(r"[A-Za-z]{2,}", t)}
|
||||
return out - _STOP
|
||||
|
||||
|
||||
def same_name(imported: dict, base: dict) -> bool:
|
||||
"""뜻 근거 — 품셈 절 번호가 같거나 이름 낱말이 겹침(값만 같은 남의 기초를 확정으로 안 묶음)."""
|
||||
secs = sections([x.get("글") for x in (imported.get("가져온") or {}).get("추정") or []])
|
||||
if secs & sections([base.get("출처")]):
|
||||
return True
|
||||
return bool(_grams([imported.get("이름")]) & _grams([base.get("이름")]))
|
||||
|
||||
|
||||
def _unnamed(imported: dict, base: dict) -> dict:
|
||||
return {"항": "이름 다름", "글": f"{imported.get('이름')} ↔ {base.get('이름')}"}
|
||||
|
||||
|
||||
def _plain(d: dict) -> dict:
|
||||
return {k: (None if v is None else float(v)) for k, v in d.items()}
|
||||
|
||||
Reference in New Issue
Block a user