Files
Aislo/M02_MasterTemplete/M02_Elem_Alias.py
T
eomsangdonandClaude Opus 5.5 dd9ebad8d1 feat(830-10): 불러오기 재료 찾기를 지역 · 상하차 칸에 맞춤 — 가져온 줄 글은 빌더 규칙으로 판독
- 새 M02_Mat_Place: 가져온 이름 · 규격 → (뺀 이름, 뺀 규격, 지역, 상하차)(material_merge 판독 한 벌) · 고르는 차례 rank
- 고르는 차례 = 상하차 맞음(글 없으면 -) › 값 있는 줄 › 줄 지역 › 전국 · 지역 모름 › 서울 › 경기 › 그 밖(값 가운데에 가까운 줄)
- Elem_Alias(_same · one · region_of) · Hopyo_Match(목록에 지역 · 상하차 · 값 · 이름 같음 = 뺀 이름 + 지역 · 상하차 같음) · Hopyo_Material(_places · 변형 지역 = 지역 칸 · _prefer) · MatSame._pick · MatFind 후보 글 = 지역 칸
- 다시 고르기 · 찾기 창 · 줄 풍선말 후보 글에 지역 · 상하차(M01 placeNote 한 벌)
- 시험: 814_1 · m02_hopyo_match 기대를 지역 칸 꼴로 · 새 지킴 test_830_10_material_region 21
- 불러오기 지킴 812_1 · 814_1 · 815_1 · 817_1 · 827_2 · 827_3 · 828_1 · 828_2 · 778_5 ×2 · 793_2 ×2 · 797_1 ×2 · m02_hopyo_match 통과 · typecheck 0

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

126 lines
5.6 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…」 같은 줄 ·
공사 직접 번호(M0000000…)는 빼고 이름 길
- `exact(이름, 규격)` = 지역 · 인도 조건 글 뗀 이름 · 규격(`M02_Mat_Place.line`)이 `master_alias.norm` 으로 같은
자재 줄(가까운 규격 아님)
- `one(줄, 키들)` = 같은 품목(지역 · 인도 조건 글 뗀 이름 · 규격 같음)의 지역 · 상하차 줄 여럿 → 상하차 › 줄 지역 ›
전국 › 서울 › 경기 › 같은 단위 › 그 밖 값 가운데 › 키 차례 하나(`M02_Mat_Place.rank` · 830-10) · 다른 품목이
섞이면 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
from M02_MasterTemplete import M02_Mat_Place as place
import master_alias as am # noqa: E402 — M01 store 가 scripts 경로를 넣음
MARKET = "MT"
PRICES = (
"물가자료",
"유통물가",
"물가정보",
"거래가격",
"일반가격",
) # 자재 사급 값 칸 다섯(조달청 뺌)
_CODE = re.compile(r"\bM\d{10}\b")
LOCAL = "M0000000" # 공사 직접 번호(파일마다 다른 품목 · 거창 치즐 = 우리 표 Air Hammer) = 코드로 안 이음
def region_of(name: Any) -> tuple[str, str]:
"""가져온 자재 이름 → (지역 · 인도 조건 글 뗀 이름, 지역 | 빈 글) — 우리 줄 지역은 지역 칸."""
got = place.line(name, "")
return got["이름"], "" if got["지역"] == place.NONE else got["지역"]
def _same(name: Any, spec: Any) -> tuple[str, str]:
got = place.line(name, spec)
return am.norm(got["이름"]), am.norm(got["규격"])
@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 priced(key: Any) -> bool:
"""우리 자재 줄에 값 칸 다섯 중 하나라도 있음."""
return any(row(MARKET, key).get(c) is not None for c in PRICES)
def est_code(code: Any) -> list[str]:
"""EST 자재 코드(M + 수 열) → 우리 자재 키들(출처 칸 같은 코드)."""
text = str(code or "").strip()
if not _CODE.fullmatch(text) or text.startswith(LOCAL):
return []
return list(book()["코드"].get(text, []))
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:
"""같은 품목 지역 · 상하차 줄 여럿 → 하나(`place.rank` 상하차 › 지역 차례 › 같은 단위 › 값 가운데 › 키) ·
다른 품목 섞임 = 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
want, unit = place.line(line.get("이름"), line.get("규격")), am.norm(line.get("단위"))
mid = place.middle(list(rows.values()))
def rank(k: str) -> tuple:
*head, gap = place.rank(rows[k], want["지역"], want["상하차"], mid)
return (*head, am.norm(rows[k].get("단위")) != unit, gap, k)
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, "")