refactoring
This commit is contained in:
150
core/normalizer.py
Normal file
150
core/normalizer.py
Normal file
@@ -0,0 +1,150 @@
|
||||
"""
|
||||
core/normalizer.py — 모든 정규화 함수의 단일 진입점
|
||||
|
||||
팀명, 구장, 포지션, 선수명, 경기유형 등의 정규화를 담당합니다.
|
||||
Playwright 의존성 없이 순수 파이썬 로직만 포함합니다.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
from core.config_loader import (
|
||||
team_name_map,
|
||||
team_code_map,
|
||||
stadium_name_map,
|
||||
game_type_map,
|
||||
position_number_map,
|
||||
position_to_defense_no,
|
||||
)
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# 팀/구장/경기유형 정규화
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
def normalize_team_name(name: str) -> str:
|
||||
"""팀명 정규화 (네이버 표기 → 관리자 사이트 표기)"""
|
||||
return team_name_map().get(name, name)
|
||||
|
||||
|
||||
def normalize_team_code(code: str) -> str:
|
||||
"""팀 코드 → 한글 팀명"""
|
||||
return team_code_map().get(code, code)
|
||||
|
||||
|
||||
def normalize_game_type(name: str) -> str:
|
||||
"""경기 유형 정규화"""
|
||||
return game_type_map().get(name, name)
|
||||
|
||||
|
||||
def normalize_stadium_name(name: str) -> str:
|
||||
"""구장명 정규화 (네이버 표기 → 관리자 사이트 select 라벨)"""
|
||||
return stadium_name_map().get(name, name)
|
||||
|
||||
|
||||
def normalize_position_to_number(position: str) -> str:
|
||||
"""포지션명 → 번호 문자열 (투수→1, 포수→2, ...)"""
|
||||
return position_number_map().get(position, "")
|
||||
|
||||
|
||||
def normalize_position_to_defense_no(position: str) -> str:
|
||||
"""포지션명 → 수비번호 (라인업 select 옵션 value)"""
|
||||
return position_to_defense_no().get(position, "")
|
||||
|
||||
|
||||
def position_label_from_number(number: str) -> str:
|
||||
"""수비번호 → 포지션명 (역매핑)"""
|
||||
pos_map = position_number_map()
|
||||
reverse = {v: k for k, v in pos_map.items()}
|
||||
return reverse.get(number, "")
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# 선수명/번호 정규화
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
def normalize_player_name(name: str | None) -> str:
|
||||
"""선수명 정규화: *, 괄호 내용 제거"""
|
||||
text = (name or "").replace("*", "").strip()
|
||||
text = re.sub(r"\([^)]*\)\s*$", "", text).strip()
|
||||
return text
|
||||
|
||||
|
||||
def normalize_lineup_text(text: str) -> str:
|
||||
"""라인업 텍스트에서 순수 이름만 추출
|
||||
|
||||
'[10] 문보경' / '문보경 [10번]' 등 → '문보경'
|
||||
"""
|
||||
text = (text or "").strip()
|
||||
text = text.replace("*", "")
|
||||
text = re.sub(r"\[\d+(?:번)?\]", "", text)
|
||||
text = re.sub(r"\s*\(.*?\)\s*", "", text)
|
||||
text = "".join(re.findall(r"[가-힣A-Za-z]+", text))
|
||||
return text.strip()
|
||||
|
||||
|
||||
def normalize_number_text(number: str | int | None) -> str:
|
||||
"""등번호 정규화: 숫자만 추출"""
|
||||
text = str(number or "").strip()
|
||||
digits = "".join(char for char in text if char.isdigit())
|
||||
if not digits:
|
||||
return ""
|
||||
return str(int(digits))
|
||||
|
||||
|
||||
def normalize_option_player_text(text: str) -> tuple[str, str]:
|
||||
"""select option 텍스트에서 선수명과 번호 분리
|
||||
|
||||
'문보경 [10번]' → ('문보경', '10')
|
||||
"""
|
||||
stripped = " ".join(text.split())
|
||||
matched = re.match(r"^(.*?)\s*\[(\d+)번\]$", stripped)
|
||||
if matched:
|
||||
return normalize_player_name(matched.group(1)), normalize_number_text(matched.group(2))
|
||||
return normalize_player_name(stripped), ""
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# 시간 유틸
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
def split_time(iso_time: str | None) -> tuple[str, str]:
|
||||
"""ISO 시간 문자열에서 시/분 분리
|
||||
|
||||
'2026-04-14T18:30:00' → ('18', '30')
|
||||
"""
|
||||
if not iso_time:
|
||||
return "00", "00"
|
||||
from datetime import datetime
|
||||
dt = datetime.fromisoformat(iso_time)
|
||||
return f"{dt.hour:02d}", f"{dt.minute:02d}"
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────
|
||||
# 텍스트 추론 유틸
|
||||
# ──────────────────────────────────────────────
|
||||
|
||||
def infer_option_role_hint(text: str) -> str:
|
||||
"""select option 텍스트에서 역할 힌트 추출
|
||||
|
||||
'문보경 (투) [10번]' → 'pitcher'
|
||||
'문보경 (타)' → 'batter'
|
||||
"""
|
||||
stripped = " ".join(text.split())
|
||||
matched = re.search(r"\(([^)]*)\)\s*(?:\[\d+번\])?$", stripped)
|
||||
if not matched:
|
||||
return ""
|
||||
hint = matched.group(1).strip()
|
||||
if hint == "투":
|
||||
return "pitcher"
|
||||
if hint == "타":
|
||||
return "batter"
|
||||
return ""
|
||||
|
||||
|
||||
def infer_target_role_hint(position_name: str | None) -> str:
|
||||
"""포지션명에서 역할 힌트 추론"""
|
||||
if position_name == "투수":
|
||||
return "pitcher"
|
||||
return "batter"
|
||||
Reference in New Issue
Block a user