151 lines
5.1 KiB
Python
151 lines
5.1 KiB
Python
"""
|
|
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"
|