feat(master_data): 관급 짝짓기 규칙 넓힘 · 단위 시험 · 호출 오류 키 가림
- 아스콘 규격 괄호 조합 이름 떼기 · 시군 묶음 지역 짝짓기 · 지난번 못 채운 줄 재시도 - 관급 열 채움 3,045 · null 1,507 (보류 394 · 못 채움 1,113) - test_masterdata_procure.py 7건 · 설계 문서에 제조사 선택지 · 로직 일괄 시험 전후 (1,024/327 동일) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PAdA5ThqmtVSsbzjusk1cJ
This commit is contained in:
@@ -53,7 +53,10 @@ KEEP = (
|
||||
def call(op: str, key: str, **kw) -> dict:
|
||||
q = "&".join(f"{k}={urllib.parse.quote(str(v))}" for k, v in kw.items())
|
||||
url = f"{BASE}{op}?serviceKey={key}&type=json&{q}"
|
||||
return json.loads(cc.fetch(url, timeout=90).decode("utf-8", "replace"))["response"]
|
||||
try:
|
||||
return json.loads(cc.fetch(url, timeout=90).decode("utf-8", "replace"))["response"]
|
||||
except Exception as e: # 오류 글에 받은 주소(키 포함)가 실려도 키는 가림
|
||||
raise RuntimeError(f"{op} 호출 실패: {str(e).replace(key, '***')}") from None
|
||||
|
||||
|
||||
def fetch_all() -> Path:
|
||||
@@ -152,6 +155,9 @@ def strength(text: str):
|
||||
return tuple(int(x) for x in m.groups()) if m else None
|
||||
|
||||
|
||||
PROVINCE = frozenset(w for k, v in ALIAS.items() for w in places(k) | places(v))
|
||||
|
||||
|
||||
def region_pick(cands: dict, want: frozenset):
|
||||
"""cands = {지명묶음: 줄} — 같은 묶음 → 겹침 가장 큰 하나(동점이면 None)."""
|
||||
if want in cands:
|
||||
@@ -159,6 +165,12 @@ def region_pick(cands: dict, want: frozenset):
|
||||
best = sorted(((len(want & k) / len(want | k), k) for k in cands if want & k), reverse=True)
|
||||
if best and best[0][0] >= 0.5 and (len(best) == 1 or best[0][0] > best[1][0]):
|
||||
return cands[best[0][1]], f"지역 비슷함 {best[0][0]:.2f}"
|
||||
# 다산 묶음이 조달청 시군 줄 여럿을 합친 것 — 묶음 안 시군마다 조달청 줄이 있을 때만(값이 갈리면 pick 이 보류)
|
||||
towns = want - PROVINCE
|
||||
if len(towns) > 1:
|
||||
hit = [k for k in cands if k <= want and k - PROVINCE]
|
||||
if towns <= set().union(*hit) if hit else False:
|
||||
return [r for k in hit for r in cands[k]], f"지역 묶음 {len(hit)}줄"
|
||||
return None, "지역 못 찾음"
|
||||
|
||||
|
||||
@@ -232,8 +244,10 @@ class Matcher:
|
||||
def _region(table: dict, name: str):
|
||||
want = places(name.split(")-", 1)[1] if ")-" in name else name)
|
||||
cands, why = region_pick(table, want)
|
||||
if cands is None:
|
||||
return None, why
|
||||
if cands is None or (
|
||||
why.startswith("지역 묶음") and "제외" in name
|
||||
): # 「~ 제외」 묶음은 짝짓지 않음
|
||||
return None, why if cands is None else "지역 못 찾음"
|
||||
corp = re.findall(
|
||||
r"([가-힣]+)레미콘\(협\)", name
|
||||
) # 이름에 조합이 붙은 줄(「제주…레미콘(협)」)은 그 조합 줄만
|
||||
@@ -253,7 +267,10 @@ class Matcher:
|
||||
r, how = pick(c)
|
||||
return r, f"{why} · {how}"
|
||||
if re.match(r"(순환)?아스콘\(관급\)-", n):
|
||||
k = (n.startswith("순환"), re.sub(r"\s+", "", spec.split("조합공통품목,")[-1]))
|
||||
k = (
|
||||
n.startswith("순환"),
|
||||
re.sub(r"\s+", "", re.split(r"조합공통품목(?:\([^)]*\))?,", spec)[-1]),
|
||||
)
|
||||
if k not in self.asc:
|
||||
return None, "규격 없음(원문 규격이 조달청에 없음)"
|
||||
c, why = self._region(self.asc[k], n)
|
||||
@@ -339,7 +356,9 @@ def refill(rows: list[dict], raw: dict) -> list[dict]:
|
||||
"""조달 값(관급 열)이 있는 줄마다 조달청 값으로 다시 채운 결과."""
|
||||
m, out = Matcher(raw), []
|
||||
for x in rows:
|
||||
if x["관급"] is None:
|
||||
if x["관급"] is None and not (x["비고"] or "").startswith(
|
||||
"관급:"
|
||||
): # 지난번에 못 채운 줄도 다시 시도
|
||||
continue
|
||||
r, why = m.find(x)
|
||||
out.append(
|
||||
|
||||
Reference in New Issue
Block a user