- 새 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>
79 lines
3.4 KiB
Python
79 lines
3.4 KiB
Python
"""불러오기 재료 지역 · 상하차 한 벌(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)
|