"""불러오기 재료 지역 · 상하차 한 벌(PLAN 830-10 · 요구 839) — 서버 단독 · 읽기만. - `line(이름, 규격)` = 가져온 줄 글(경쟁사 이름 · 규격 — 지역 · 인도 조건 글이 그대로 옴)을 M01 재료 표 빌더와 같은 규칙(`material_merge.split_load` · `split_region`)으로 읽음 → {이름, 규격, 지역, 상하차} · 글 없음 = -. - 우리 줄 = M01 재료 표 지역 · 상하차 칸(이름 글로 읽지 않음). - `rank(우리 줄, 바라는 것, 가운데 값)` = 같은 품목 줄 여럿에서 고르는 차례 — 상하차가 줄 글과 같음(글 없으면 -) › 값 있는 줄(M01 검색어 고르기와 같음) › 지역: 줄 지역 › 전국(지역 모름 - 포함) › 서울 › 경기 › 그 밖(값이 가운데 값에 가까운 줄) — 조달청 지역 규칙과 같은 차례(요구 830). """ from __future__ import annotations import re from statistics import median from typing import Any from M01_MasterData import M01_MasterData_Store as m01 # noqa: F401 — scripts 경로를 넣음 import material_merge as mmerge # noqa: E402 NONE = mmerge.NONE NATION = ("전국", "전지역", NONE) PRICES = ("물가자료", "유통물가", "물가정보", "거래가격", "일반가격") # 사급 값 칸(조달청 뺌) def line(name: Any, spec: Any) -> dict[str, str]: """가져온 줄 → {이름, 규격(지역 · 인도 조건 글 뺌), 지역, 상하차}.""" n, s, load, _ = mmerge.split_load({"이름": str(name or ""), "규격": str(spec or "")}) n, s, where = mmerge.split_region(n, s) return {"이름": n, "규격": s, "지역": where, "상하차": load} def words(where: Any) -> list[str]: """지역 글 → 한글 낱말(전국류 뺌) — 「경상북도(칠곡군)」 → 경상북도 · 칠곡군.""" return [w for w in re.findall(r"[가-힣]{2,}", str(where or "")) if w not in NATION] def hit(where: Any, want: Any) -> bool: """우리 지역 글이 줄 지역 글과 맞음 — 같은 글 · 낱말 앞 두 글자가 하나라도 같음(「경북」 · 「경북제일」).""" if not want or want == NONE: return False if str(where) == str(want): return True mine = words(want) if isinstance(want, str) else list(want) return any(a[:2] == b[:2] for a in mine for b in words(where)) def value(row: dict) -> Any: """사급 값 칸 다섯 가운데 낮은 값(줄에 「값」 이 있으면 그것) — 없음 = None.""" if "값" in row: return row["값"] got = [row[c] for c in PRICES if row.get(c) is not None] return min(got) if got else None def middle(rows: list[dict]) -> Any: got = [v for v in (value(r) for r in rows) if v is not None] return median(got) if got else None def rank(row: dict, want_where: Any, want_load: str, mid: Any) -> tuple: """고르는 차례 열쇠(작을수록 먼저) — (상하차 다름, 값 없음, 지역 차례, 가운데 값과의 거리).""" where = str(row.get("지역") or NONE) if hit(where, want_where): tier = 0 elif where in NATION or not words(where): tier = 1 else: tier = 2 if where.startswith("서울") else 3 if where.startswith("경기") else 4 v = value(row) gap = ( 0.0 if tier < 4 or mid is None else float("inf") if v is None else abs(float(v) - float(mid)) ) return ((row.get("상하차") or NONE) != (want_load or NONE), v is None, tier, gap)