""" automation/pitch_input.py — 투구 입력 개별 투구의 구종, 구속, 투구결과를 사이트에 입력합니다. """ from __future__ import annotations from typing import Any from playwright.sync_api import Page from core.config_loader import pitch_type_map, pitch_result_map from automation.page_helpers import set_radio_by_label def get_pitch_runner_events( pitch: dict[str, Any], event: dict[str, Any] | None = None, ) -> list[dict[str, Any]]: """투구에 연결된 주루 이벤트 반환""" pitch_runner_events = list(pitch.get("runnerEvents") or []) if pitch_runner_events: return pitch_runner_events if event and event.get("runnerEvents"): return list(event.get("runnerEvents") or []) return [] def set_pitch(page: Page, pitch: dict[str, Any], event: dict[str, Any] | None = None) -> None: """투구 하나를 사이트에 입력 (구종 + 결과 + 구속)""" pt_map = pitch_type_map() pr_map = pitch_result_map() pitch_type = pt_map.get(pitch.get("pitchType") or "") pitch_result_text = (pitch.get("pitchResultText") or "").strip() normalized_text = pitch_result_text.replace(" ", "") # 피치클락 투수위반 → 볼 if "피치클락" in pitch_result_text and "투수위반" in pitch_result_text: pitch_result_text = "볼" # 폭투/포일 체크 runner_events = get_pitch_runner_events(pitch, event) is_wild_pitch = any( re_.get("type") == "wild_pitch_advance" or "폭투" in (re_.get("text") or "") for re_ in runner_events ) is_passed_ball = any( re_.get("type") == "passed_ball_advance" or "포일" in (re_.get("text") or "") for re_ in runner_events ) if is_wild_pitch: pitch_result = "폭투-볼" elif is_passed_ball: pitch_result = "포일-볼" else: if "번트" in normalized_text and "헛스윙" in normalized_text: pitch_result = "번트시도-스트라이크" elif "번트" in normalized_text and "파울" in normalized_text: pitch_result = "번트-파울" else: pitch_result = pr_map.get(pitch_result_text) if not pitch_result and pitch.get("pitchResult") in {"BS", "V"}: pitch_result = "번트시도-스트라이크" if not pitch_result and pitch.get("pitchResult") == "BF": pitch_result = "번트-파울" if not pitch_result and "고의사구" in pitch_result_text: pitch_result = "고의사구" if not pitch_result and "파울플라이" in pitch_result_text and "실책" in pitch_result_text: pitch_result = "파울플라이-실책" # 구종 입력 if pitch_type: set_radio_by_label(page, "evt_ballType", pitch_type) # 투구 결과 입력 if pitch_result: set_radio_by_label(page, "evt_batter", pitch_result) # 구속 입력 speed_input = page.locator("#ballspeed") speed_input.fill(str(pitch.get("speedKmh") or 0)) speed_input.evaluate("node => node.dispatchEvent(new Event('change', {bubbles:true}))") page.wait_for_timeout(50) def set_pitch_meta_only(page: Page, pitch: dict[str, Any]) -> None: """구종/구속만 세팅 (인플레이 마지막 구에서 사용) evt_batter를 건드리지 않아 팝업이 미리 열리는 것을 방지. """ pt_map = pitch_type_map() pitch_type = pt_map.get(pitch.get("pitchType") or "") if pitch_type: set_radio_by_label(page, "evt_ballType", pitch_type) page.locator("#ballspeed").fill(str(pitch.get("speedKmh") or 0))