Files
Aislo/M02_MasterTemplete/M02_Elem_Alias.py
T
eomsangdonandClaude Opus 5.5 030a843f07 feat(815-1 K1·K2·K3·K5): 불러오기 이음 — EST 코드 · 별칭 칸 · 표기 맞추기 · 견적 줄
- K1 EST 원파일 자재 = 우리 자재 출처 칸 EST 코드로 바로 이음(새 M02_Elem_Alias · 자재 자원 24,219 중 코드 같음 23,981 모두 그 줄)
- K2 M01 요소 줄 「별칭」 칸 읽기 — 기계 찾기(옛 별칭 장부 읽기 걷음) · 재료 · 인력 · 유가 · 일위대가 호표 줄(Units · Hopyo_Sangeun 장부 import 뗌)
- K3 재료 표기 정규화 정확 일치(M/M · 곱셈표 · 지름 · 괄호 · 빈칸 · 전각 · 지역 꼬리) — 바탕 이름 길보다 먼저(치즐(0.7m3) → 0.2㎥ 줄로 잘못 잇던 것 바로잡음)
- K5 견적 · 운임 · 수수료 줄 = 우리 자재면 재료 줄(우리 값) · 아니면 그대로 + 「견적」 표지 · 문서 설명 · 화면 「견적 n」 배지(못 이음 수에서 뺌)
- 시험 test_815_1_import_link 새로 · test_812_9 = 옛 장부 별칭 대신 기계 줄 별칭 칸(마스터 다섯은 칸이 비면 시험 동안만 넣음) · 「디젤 엔진」 은 마스터 별칭 묶음으로 옮김
- test_778_5 기계 표(162 · 3 · 25)는 고치기 전 판과 같음 — 812-9 뒤부터 굳힌 수와 다름(이번 고침 무관 · 안 고침)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-11 14:36:58 +09:00

107 lines
4.8 KiB
Python

"""M02 불러오기 요소 잇기 색인(PLAN 815-1 · 요구 755 ~ 764) — M01 요소 줄 「별칭」 칸 · EST 참조 코드 ·
재료 정확 일치. 서버 단독 · 읽기만(M01 판이 바뀌면 다시 읽음 · `id(whole)`).
- `keys(표, 이름, 규격)` = 별칭으로 이어지는 키들(풀이 = `master_alias` · 표 LB 인력 · EQ 기계 · MT 재료 ·
MO 유가) — 부르는 곳: 단가산출 불러오기(`M02_San_Link.m01`) · 1차 줄 판정(`M02_Hopyo_Row.m01_picks`) ·
기계 찾기(`M02_Machine_Find.alias`) · 일위대가 호표 줄(`M02_Hopyo_Units`)
- `est_code(코드)` = EST 원파일 자재 자원 코드(M…) ↔ 우리 자재 줄 `출처` 칸 「자재품목 M…」 같은 줄
- `exact(이름, 규격)` = 지역 꼬리 뗀 이름 · 규격이 `master_alias.norm` 으로 같은 자재 줄(가까운 규격 아님)
- `one(줄, 키들)` = 같은 품목(지역 꼬리 뗀 이름 · 규격 같음)의 지역 줄 여럿 → 줄 지역 › 서울 › 같은 단위 ›
키 차례 하나 · 다른 품목이 섞이면 None(후보 여럿)
"""
from __future__ import annotations
import re
from functools import lru_cache
from typing import Any
from M01_MasterData import M01_MasterData_Store as m01
import master_alias as am # noqa: E402 — M01 store 가 scripts 경로를 넣음
MARKET = "MT"
BASE_REGION = "서울"
_CODE = re.compile(r"\bM\d{10}\b")
_REGION = re.compile(r"\s*,\s*([가-힣]{2,4})\s*$") # 자재 이름 지역 꼬리 「, 서울」
def region_of(name: Any) -> tuple[str, str]:
"""자재 이름 → (지역 꼬리 뗀 이름, 지역 | 빈 글)."""
text = str(name or "")
m = _REGION.search(text)
return (text[: m.start()], m.group(1)) if m else (text, "")
def _same(name: Any, spec: Any) -> tuple[str, str]:
return am.norm(region_of(name)[0]), am.norm(spec)
@lru_cache(maxsize=2)
def _book(_whole: int) -> dict[str, Any]:
whole = m01.loaded()[1]
tables = {t: whole.numbers.get(t, {}) for t in ("LB", "EQ", "MO")}
tables[MARKET] = whole.index.get(MARKET, {})
rows = {t: {str(r.get("키") or k): r for k, r in got.items()} for t, got in tables.items()}
alias = {t: am.index({"키": k, "별칭": r.get("별칭")} for k, r in got.items()) for t, got in rows.items()} # fmt: skip
code: dict[str, list[str]] = {}
exact: dict[tuple[str, str], list[str]] = {}
for k, r in rows[MARKET].items():
for c in _CODE.findall(str(r.get("출처") or "")):
code.setdefault(c, []).append(k)
exact.setdefault(_same(r.get("이름"), r.get("규격")), []).append(k)
return {"줄": rows, "별칭": alias, "코드": code, "같음": exact}
def book() -> dict[str, Any]:
return _book(id(m01.loaded()[1]))
def row(table: str, key: Any) -> dict[str, Any]:
return book()["줄"].get(table, {}).get(str(key)) or {}
def keys(table: str, name: Any, spec: Any = "") -> list[str]:
"""별칭 칸으로 이어지는 키들(없음 = [])."""
b = book()
got = b["줄"].get(table, {})
return am.match(b["별칭"].get(table, {}), lambda k: (got.get(k) or {}).get("규격"), name, spec)
def est_code(code: Any) -> list[str]:
"""EST 자재 코드(M + 수 열) → 우리 자재 키들(출처 칸 같은 코드)."""
text = str(code or "").strip()
return list(book()["코드"].get(text, [])) if _CODE.fullmatch(text) else []
def exact(name: Any, spec: Any) -> list[str]:
"""지역 꼬리 뗀 이름 · 규격 정규화 같음 자재 키들."""
return list(book()["같음"].get(_same(name, spec), []))
def one(line: dict[str, Any], found: list[str]) -> str | None:
"""같은 품목 지역 줄 여럿 → 하나(줄 지역 › 서울 › 같은 단위 › 키) · 다른 품목 섞임 = None."""
rows = {k: row(MARKET, k) for k in found}
if not rows or len({_same(r.get("이름"), r.get("규격")) for r in rows.values()}) != 1:
return None
mine, unit = region_of(line.get("이름"))[1], am.norm(line.get("단위"))
def rank(k: str) -> tuple:
where = region_of(rows[k].get("이름"))[1]
return (where != mine if mine else True, where != BASE_REGION, am.norm(rows[k].get("단위")) != unit, k) # fmt: skip
return min(rows, key=rank)
def material(line: dict[str, Any], code: Any = None) -> tuple[str | None, str]:
"""재료 줄 → (우리 자재 키, 길 「코드」 · 「별칭」 · 「정확 일치」) · 별칭 여럿 = (None, 「별칭 후보 여럿」) ·
없음 = (None, "")."""
if hit := est_code(code):
return one(line, hit) or hit[0], "코드"
hit = keys(MARKET, line.get("이름"), line.get("규격"))
if hit:
got = hit[0] if len(hit) == 1 else one(line, hit)
return (got, "별칭") if got else (None, "별칭 후보 여럿")
got = one(line, exact(line.get("이름"), line.get("규격")))
return (got, "정확 일치") if got else (None, "")