from __future__ import annotations import argparse from pathlib import Path from playwright.sync_api import Error, Playwright, sync_playwright from browser_launch import launch_browser_context from register_game_playwright import ( DEFAULT_BASE_URL, DEFAULT_GAME_ID, DEFAULT_REPORT_DIR, load_report, open_edit_page, resolve_end_time, ) TARGET_GAME_ID = DEFAULT_GAME_ID TARGET_MANAGER_GAME_NO = "" TARGET_REPORT_PATH = "" TARGET_END_TIME = "" TARGET_ATTENDANCE = "" TARGET_SAVE = True def parse_args() -> argparse.Namespace: parser = argparse.ArgumentParser( description="기존 경기 수정 화면에서 경기 후 메타 정보만 입력합니다." ) parser.add_argument("--game-id", default=TARGET_GAME_ID, help="예: 20260404LGWO02026") parser.add_argument("--report-path", help="기본값: output/_report.json") parser.add_argument("--base-url", default=DEFAULT_BASE_URL, help="관리자 사이트 기본 URL") parser.add_argument( "--manager-game-no", default=(TARGET_MANAGER_GAME_NO or None), help="관리자 게임번호. 있으면 해당 행의 수정 버튼으로 진입", ) parser.add_argument("--user-data-dir", default="playwright-user-data", help="Chromium 사용자 데이터 폴더") parser.add_argument("--channel", default="chrome", help="브라우저 채널. 예: chrome, msedge") parser.add_argument("--end-time", default=(TARGET_END_TIME or None), help="종료시간 HH:MM") parser.add_argument("--attendance", default=(TARGET_ATTENDANCE or None), help="관중 수") parser.add_argument("--headless", action="store_true", help="헤드리스 모드") parser.add_argument("--save", dest="save", action="store_true", help="수정 버튼까지 클릭") parser.add_argument("--no-save", dest="save", action="store_false", help="저장 직전까지만 입력") parser.add_argument("--close", action="store_true", help="작업 후 브라우저를 닫음") parser.set_defaults(save=TARGET_SAVE) return parser.parse_args() def resolve_report_path(args: argparse.Namespace) -> Path: if args.report_path: return Path(args.report_path) if TARGET_REPORT_PATH: return Path(TARGET_REPORT_PATH) return DEFAULT_REPORT_DIR / f"{args.game_id}_report.json" def fill_post_game_form(page, report: dict, args: argparse.Namespace) -> None: game_info = report["game_info"] end_hour, end_minute = resolve_end_time( game_info, argparse.Namespace( end_time=args.end_time, end_hour=None, end_minute=None, ), ) attendance_source = args.attendance if args.attendance is not None else game_info.get("attendance") attendance = "".join(char for char in str(attendance_source or "") if char.isdigit()) if attendance: page.locator("#spectatorCnt").fill(attendance) page.locator("#endH").select_option(value=str(int(end_hour))) page.locator("#endM").select_option(label=end_minute) umpires = game_info["umpires"] page.locator("#homeplate_umpire").fill(umpires["chief"] or "") page.locator("#base_umpire1").fill(umpires["first_base"] or "") page.locator("#base_umpire2").fill(umpires["second_base"] or "") page.locator("#base_umpire3").fill(umpires["third_base"] or "") def run(playwright: Playwright, args: argparse.Namespace) -> None: report = load_report(resolve_report_path(args)) browser = launch_browser_context( playwright=playwright, user_data_dir=args.user_data_dir, channel=args.channel, headless=args.headless, ) page = browser.pages[0] if browser.pages else browser.new_page() try: open_edit_page(page, args.base_url, report, args.manager_game_no) fill_post_game_form(page, report, args) if args.save: page.evaluate("""() => { window.confirm = () => true; window.alert = () => {}; }""") page.locator("#gameUpdateBtn").click() page.wait_for_timeout(1000) if args.close: browser.close() return try: page.wait_for_timeout(3600 * 1000) except KeyboardInterrupt: pass except Error as exc: if "Target page, context or browser has been closed" not in str(exc): raise pass finally: try: browser.close() except Exception: pass def main() -> None: args = parse_args() with sync_playwright() as playwright: run(playwright, args) if __name__ == "__main__": main()