""" automation/game_end_input.py — 경기 종료 처리 투수들의 승패/홀드/세이브 기록 등을 경기 종료 팝업에 입력하고 저장합니다. """ from __future__ import annotations from typing import Any from playwright.sync_api import Page from automation.page_helpers import show_debug_overlay from automation.lineup_input import normalize_lineup_text def _open_game_end_popup(page: Page) -> None: """경기 종료 팝업 열기""" page.evaluate("""() => { window.confirm = () => true; window.alert = () => {}; }""") page.locator("#gameEndBtn").click(force=True) page.wait_for_selector("#btnGameEnd", timeout=5000) page.wait_for_selector("input[name^='homeTeamPitcher_'], input[name^='awayTeamPitcher_']", timeout=5000) def _get_game_end_pitcher_rows(page: Page) -> dict[str, list[dict[str, Any]]]: """팝업 내 홈/원정 투수 리스트 추출""" return page.evaluate( """() => { const rowsFor = (nameAttr) => { return [...document.querySelectorAll(`input[name='${nameAttr}']`)].map((input, idx) => { const tr = input.closest('tr'); const firstTd = tr ? tr.querySelector('td') : null; return { idx, name: firstTd ? firstTd.textContent.trim() : '', }; }); }; return { home: rowsFor('home_player_id'), away: rowsFor('away_player_id'), }; }""" ) def _select_game_end_role(page: Page, side: str, idx: int, role_value: str) -> None: """특정 투수의 역할(승/패/홀/세 등) 라디오 버튼 선택""" selector = f"input[name='{side}TeamPitcher_{idx}'][value='{role_value}']" ok = page.evaluate( """(selector) => { const node = document.querySelector(selector); if (!node) return false; node.disabled = false; node.checked = true; node.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true })); node.dispatchEvent(new Event('change', { bubbles: true })); return node.checked === true; }""", selector, ) if not ok: page.locator(selector).click(force=True) def _check_game_end_blown_save(page: Page, side: str, idx: int) -> None: """블론세이브 체크박스 선택""" selector = f"input[name='{side}BlownSave_{idx}']" ok = page.evaluate( """(selector) => { const node = document.querySelector(selector); if (!node) return false; node.disabled = false; node.checked = true; node.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true })); node.dispatchEvent(new Event('change', { bubbles: true })); return node.checked === true; }""", selector, ) if not ok: page.locator(selector).check(force=True) def fill_game_end_pitching(page: Page, report: dict[str, Any]) -> None: """리포트의 투수 요약을 바탕으로 경기 종료 팝업 폼 채우기""" _open_game_end_popup(page) rows = _get_game_end_pitcher_rows(page) summary = report.get("pitching_summary") or {} lineups = report.get("lineups") or {} home_starter = normalize_lineup_text(((lineups.get("home_team") or {}).get("starter_pitcher") or {}).get("name") or "") away_starter = normalize_lineup_text(((lineups.get("away_team") or {}).get("starter_pitcher") or {}).get("name") or "") winners = {normalize_lineup_text(name) for name in (summary.get("승리투수") or [])} losers = {normalize_lineup_text(name) for name in (summary.get("패전투수") or [])} holds = {normalize_lineup_text(name) for name in (summary.get("홀드") or [])} saves = {normalize_lineup_text(name) for name in (summary.get("세이브") or [])} blown_saves = {normalize_lineup_text(name) for name in (summary.get("블론세이브") or [])} fixed_roles = winners | losers | holds | saves for side, side_rows in rows.items(): starter_name = home_starter if side == "home" else away_starter for row in side_rows: name = normalize_lineup_text(row.get("name") or "") idx = int(row["idx"]) if name in winners: _select_game_end_role(page, side, idx, "wins") elif name in losers: _select_game_end_role(page, side, idx, "loses") elif name in saves: _select_game_end_role(page, side, idx, "save") elif name in holds: _select_game_end_role(page, side, idx, "holds") elif name and name != starter_name and name not in fixed_roles: _select_game_end_role(page, side, idx, "re") if name in blown_saves: _check_game_end_blown_save(page, side, idx) page.wait_for_timeout(300) show_debug_overlay( page, [ "게임종료 팝업 입력 준비 완료", f"승리: {', '.join(summary.get('승리투수') or []) or '-'}", f"패전: {', '.join(summary.get('패전투수') or []) or '-'}", f"홀드: {', '.join(summary.get('홀드') or []) or '-'}", f"세이브: {', '.join(summary.get('세이브') or []) or '-'}", f"블론세이브: {', '.join(summary.get('블론세이브') or []) or '-'}", ], ) def submit_game_end(page: Page) -> None: """경기 종료 최종 완료(저장) 버튼 클릭""" page.evaluate("""() => { window.confirm = () => true; window.alert = () => {}; }""") page.locator("#btnGameEnd").click(force=True) page.wait_for_timeout(1500)