Files
Aislo/resources/master_data/scripts/master_alias.py
T

102 lines
4.4 KiB
Python
Raw 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.
"""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"[φϕΦøØ∅]|(?<![a-z가-힣])d\s*=\s*(?=\d)")
_BRACKETS = 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")