From 55ad1bfee078d40673906d930b24fe66683f9eea Mon Sep 17 00:00:00 2001 From: umsangdon Date: Wed, 11 Nov 2026 14:13:52 +0900 Subject: [PATCH] =?UTF-8?q?feat(815-1=20=EB=B3=84=EC=B9=AD):=20=EB=B3=84?= =?UTF-8?q?=EC=B9=AD=20=EC=B9=B8=20=ED=92=80=EC=9D=B4=20=EC=A0=95=EB=B3=B8?= =?UTF-8?q?=20master=5Falias=20=E2=80=94=20=EC=BD=A4=EB=A7=88=20=C2=B7=20?= =?UTF-8?q?=EC=A0=84=EA=B0=81=20=EC=BD=A4=EB=A7=88=20=C2=B7=20=ED=81=B0?= =?UTF-8?q?=EB=94=B0=EC=98=B4=ED=91=9C=20=EC=A1=B0=EA=B0=81=20=C2=B7=20?= =?UTF-8?q?=ED=91=9C=EA=B8=B0=20=EC=A0=95=EA=B7=9C=ED=99=94=20=C2=B7=20?= =?UTF-8?q?=EC=83=89=EC=9D=B8=20=C2=B7=20=EB=A7=9E=EB=8C=80=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- resources/master_data/scripts/master_alias.py | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 resources/master_data/scripts/master_alias.py diff --git a/resources/master_data/scripts/master_alias.py b/resources/master_data/scripts/master_alias.py new file mode 100644 index 000000000..ad435f38b --- /dev/null +++ b/resources/master_data/scripts/master_alias.py @@ -0,0 +1,101 @@ +"""M01 요소 「별칭」 칸 풀이 정본(PLAN 815-1 · 요구 764) — 인력 · 기계 · 재료 · 유가 줄의 `별칭` 글 하나. + +별칭 글 = 콤마(「,」 · 전각 「,」)로 여러 개 · 앞뒤 빈칸 뗌 · 큰따옴표(" · “ ”)로 감싼 조각 = 통째 +한 별칭(속 콤마 보존 — 이름과 규격을 같이 적을 때 「"결속선(철선) #20, 0.9M/M"」) · 빈 칸 = 없음. +`norm` = 표기 정규화(빈칸 · 전각 · 대소문자 · 괄호 · 단위 M/M = mm · 곱셈표 · 지름 Φ · D= · 번호 #) — +이름 낱말 순서(「결속선(철선)」 ↔ 「철선(결속선)」)는 맞추지 않음(별칭 몫). +뜻: 별칭 = 그 줄을 가리키는 다른 이름 — 이름만 적은 별칭은 줄 규격이 정규화해 같을 때 그 줄 · +이름 + 규격을 적은 별칭은 「이름 규격」 통째가 같을 때 그 줄 · 여럿 = 부르는 쪽이 「후보 여럿」. +쓰는 곳 = M02 불러오기 요소 잇기(`M02_Elem_Alias`) · M01 별칭 칸 저장 · 보기. 서버 단독 · 읽기만. +""" + +from __future__ import annotations + +import re +import unicodedata +from collections.abc import Callable, Iterable +from typing import Any + +_QUOTES = {'"': '"', "“": "”", "”": "”"} +_SEP = (",", ",") +_TIMES = re.compile(r"(?<=\d)\s*[x×*]\s*(?=\d)") +_DIAM = re.compile(r"[φϕΦøØ∅]|(?]") + + +def names(text: Any) -> list[str]: + """별칭 글 → 별칭 목록(차례 그대로 · 같은 것 한 번).""" + out: list[str] = [] + piece, close = "", None + for ch in f"{text or ''},": + if close: + if ch == close or (close == "”" and ch == '"'): + close = None + else: + piece += ch + elif ch in _QUOTES and not piece.strip(): + close, piece = _QUOTES[ch], "" + elif ch in _SEP: + one = piece.strip() + if one and one not in out: + out.append(one) + piece = "" + else: + piece += ch + if close and piece.strip() and piece.strip() not in out: # 닫는 따옴표 빠짐 = 끝까지 한 조각 + out.append(piece.strip()) + return out + + +def norm(text: Any) -> str: + """표기 정규화 — 같은 글이면 같은 값(맞대기 열쇠).""" + t = unicodedata.normalize("NFKC", str(text or "")).lower() + t = t.replace("m/m", "mm") + t = _TIMES.sub("x", t).replace("×", "x") + t = _DIAM.sub("φ", t) + t = _BRACKETS.sub("", t) + return re.sub(r"\s+", "", t) + + +def index(rows: Iterable[dict[str, Any]]) -> dict[str, list[str]]: + """요소 줄들(`키` · `별칭`) → {정규화 별칭: [키 …]}(줄 차례).""" + out: dict[str, list[str]] = {} + for row in rows: + key = row.get("키") + for one in names(row.get("별칭")) if key else (): + got = out.setdefault(norm(one), []) + if str(key) not in got: + got.append(str(key)) + return out + + +def match( + idx: dict[str, list[str]], spec_of: Callable[[str], Any], name: Any, spec: Any = "" +) -> list[str]: + """가져온 줄(이름 · 규격) → 별칭으로 이어지는 키들 — 「이름 규격」 통째 별칭 › 이름 별칭 + 줄 규격 + 정규화 같음 › (가져온 규격 빔) 이름 별칭 줄 하나 · 없음 = [].""" + mine = norm(spec) + if mine and (whole := idx.get(norm(name) + mine)): + return list(whole) + named = idx.get(norm(name)) or [] + same = [k for k in named if norm(spec_of(k)) == mine] + if same: + return same + return list(named) if not mine and len(named) == 1 else [] + + +if __name__ == "__main__": + assert names('철선, 결속선 ,"결속선(철선) #20, 0.9M/M" , ,철선') == [ + "철선", "결속선", "결속선(철선) #20, 0.9M/M" + ] # fmt: skip + assert ( + norm("결속선(철선) #20, 0.9M/M") == norm("결속선 철선#20,0.9㎜") == "결속선철선#20,0.9mm" + ) + assert norm("600×1200") == norm("600 X 1200") == norm("600*1200") == "600x1200" + assert norm("PVC D=100") == norm("PVC Φ100") == norm("pvc ø 100") == "pvcφ100" + idx = index([{"키": "A", "별칭": "철선"}, {"키": "B", "별칭": '"철선 #8"'}]) + specs = {"A": "#20, 0.9mm", "B": "#8"} + assert match(idx, specs.get, "철선", "#20, 0.9M/M") == ["A"] + assert match(idx, specs.get, "철선", "#8") == ["B"] + assert match(idx, specs.get, "철선", "#10") == [] + print("ok")