- 행정구역 표 = 시도 17(짧은 이름) + 전국 · 시도별 시군구 229(자치 227 + 제주 행정시 2 · 2026-07-01 인천 제물포 · 영종 · 검단 반영) · 옛 이름(강원도 · 전라북도 · 청원 · 마산 · 진해 · 연기 · 인천 남구 · 중구 · 동구 · 경북 군위) · 별칭(영종도 · 용유도 · 세종시 · 제주도) - 풀이 master_region.split — 시도만 = 시군구 「전체」 · 시군구만 = 시도 찾음 · 읍면 = 시군구(읍면) · 여러 시도 = 쉼표 · 제외 조건 = 끝에 「 제외」 · 조합 이름 = 비고 · 잘린 끝 낱말 = 하나뿐인 시군구(동두 → 동두천 · 금 → 금천) - 지역 글 242 종 모두 풀림(못 푼 것 0) · 옛 이름 바꾼 줄 648 · 줄 · 키 · 값 그대로(24,027 · 비고만 조합 121 줄) - 쓰는 곳: 지역 글 = material_merge.where(시도 + 시군구) — M01 찾기(label · 검색어 · 자재지역) · 표 찾기 · 후보 글 · 불러오기(M02_Mat_Place · Hopyo_Match · Hopyo_Material — 가져온 지역 낱말도 짧은 이름으로 · 시군구가 있으면 그것으로 맞댐) - M01 화면: 시도 = 고르기(목록 밖 값 「대전,충남」 은 끝에) · 시군구 = 글 · 새 줄 - · - - 로직 1,520 시험 계산 전후 다름 0 · check_master 틀 · 본문 · 로직 · 조합 · 목차 0 · 단위 27 · 배 7(전과 같음) - 빌더 adopt 가 로직 장 파일 「차례」 를 하나씩 미는 것은 이번 일 밖 — 되돌림(로직 파일 손대지 않음) - 새 지킴 test_830_11_material_sido · 낡은 시험 고침: test_830_10 · test_830_2(열 · 지역 기대를 두 칸으로 · 830-11 새 겹침 38 묶음 = 청원 → 청주 4 · 조합 비고 34) · test_m02_hopyo_match(강원도(강릉시) → 강원 강릉) - 틀 문서 _틀.md 11 절 행정구역 표 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
98 lines
4.4 KiB
Python
98 lines
4.4 KiB
Python
"""불러오기 재료 지역 · 상하차 한 벌(PLAN 830-10 · 830-11 · 요구 839 · 842) — 서버 단독 · 읽기만.
|
|
|
|
- `line(이름, 규격)` = 가져온 줄 글(경쟁사 이름 · 규격 — 지역 · 인도 조건 글이 그대로 옴)을 M01 재료 표 빌더와
|
|
같은 규칙(`material_merge.split_load` · `split_region` · `master_region.split`)으로 읽음 →
|
|
{이름, 규격, 지역(「경북 칠곡」 꼴 · `material_merge.where`), 상하차} · 글 없음 = - · 못 푸는 지역 글은 그대로.
|
|
- 우리 줄 = M01 재료 표 시도 · 시군구 · 상하차 칸(이름 글로 읽지 않음) · 지역 글 = `material_merge.where`.
|
|
- `norm(낱말)` = 가져온 글의 지역 낱말 → 짧은 이름(「경상북도」 → 경북 · 「칠곡군」 → 칠곡).
|
|
- `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 = ("전국", "전지역", mmerge.mr.WHOLE, 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)
|
|
try:
|
|
sido, sgg, _ = mmerge.mr.split(where)
|
|
where = mmerge.where({mmerge.SIDO: sido, mmerge.SIGUNGU: sgg})
|
|
except ValueError:
|
|
pass # 표에 없는 글 — 그대로
|
|
return {"이름": n, "규격": s, "지역": where, "상하차": load}
|
|
|
|
|
|
def norm(word: str) -> str:
|
|
"""지역 낱말 → 짧은 이름(시군구가 있으면 그것 · 시도만이면 시도) — 못 풀면 그대로."""
|
|
try:
|
|
sido, sgg, _ = mmerge.mr.split(word)
|
|
except ValueError:
|
|
return word
|
|
return sido if sgg in (mmerge.mr.WHOLE, NONE) else sgg
|
|
|
|
|
|
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
|
|
if isinstance(want, str): # 「경북 포항」 → 포항(시군구가 있으면 그것만) · 「경북」 → 경북
|
|
mine = words(want.split(" ", 1)[-1])
|
|
else:
|
|
mine = words(" ".join(norm(w) for w in 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 mmerge.where(row))
|
|
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)
|