refactoring
This commit is contained in:
131
core/review_parser.py
Normal file
131
core/review_parser.py
Normal file
@@ -0,0 +1,131 @@
|
||||
"""
|
||||
core/review_parser.py — 합의판정/비디오판독 파싱
|
||||
|
||||
판독 텍스트에서 항목, 원래 판정, 최종 판정 등을 추출합니다.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from typing import Any
|
||||
|
||||
from core.config_loader import review_result_groups
|
||||
|
||||
|
||||
def infer_review_item(detail_text: str) -> str:
|
||||
"""판독 텍스트에서 사이트 표준 항목 추론
|
||||
|
||||
'홈런 파울 판정' → '홈런타구 페어 파울'
|
||||
"""
|
||||
dt = detail_text.replace(" ", "")
|
||||
if "홈런" in dt:
|
||||
return "홈런타구 페어 파울"
|
||||
if "아웃" in dt or "세이프" in dt or "포스" in dt or "태그" in dt or "견제" in dt or "도루" in dt:
|
||||
return "포수/태그플레이 아웃/세이프"
|
||||
if "페어" in dt or "파울" in dt:
|
||||
return "외야타구 페어 파울"
|
||||
if "포구" in dt or "노바운드" in dt or "바운드" in dt:
|
||||
return "야수의 포구"
|
||||
if "몸에맞" in dt or "데드볼" in dt:
|
||||
return "몸에 맞는 볼"
|
||||
if "헛스윙" in dt or "스윙" in dt:
|
||||
return "헛스윙"
|
||||
return "기타"
|
||||
|
||||
|
||||
def normalize_review_result_token(token: str, review_item: str) -> str | None:
|
||||
"""판독 결과 토큰을 정규화
|
||||
|
||||
'세이프' → '세이프', '노스윙' → '불인정'
|
||||
"""
|
||||
token = (token or "").strip()
|
||||
if not token:
|
||||
return None
|
||||
|
||||
if review_item in {"홈런타구 페어 파울", "외야타구 페어 파울"}:
|
||||
if "페어" in token:
|
||||
return "페어"
|
||||
if "파울" in token:
|
||||
return "파울"
|
||||
elif review_item in {"포수/태그플레이 아웃/세이프", "야수의 포구"}:
|
||||
if "아웃" in token:
|
||||
return "아웃"
|
||||
if "세이프" in token:
|
||||
return "세이프"
|
||||
elif review_item == "헛스윙":
|
||||
# '노스윙'에도 '스윙'이 포함되므로 먼저 체크
|
||||
if "불인정" in token or "노스윙" in token or "공포" in token or "노 스윙" in token:
|
||||
return "불인정"
|
||||
if "스윙" in token or "인정" in token:
|
||||
return "인정"
|
||||
else:
|
||||
if "불인정" in token or "실패" in token:
|
||||
return "불인정"
|
||||
if "인정" in token:
|
||||
return "인정"
|
||||
return token # 모르는 키워드 → 원문 그대로
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def parse_review_event_text(text: str) -> dict[str, Any]:
|
||||
"""판독 텍스트를 파싱하여 구조화된 dict로 변환
|
||||
|
||||
입력 예: '6회초 8번타순 1구 후 18:45 ~ 18:46 (1분간) LG요청
|
||||
비디오 판독: 안중열 포스아웃 관련 세이프→세이프'
|
||||
"""
|
||||
inning_match = re.search(r"(\d+)회(초|말)", text)
|
||||
request_team_match = re.search(r"([가-힣A-Za-z]+)요청\s*(?:비디오 판독|합의 판정)", text)
|
||||
|
||||
# '→노 스윙' 같은 공백 정규화
|
||||
normalized = re.sub(r"→([가-힣]+)\s+([가-힣]+)", r"→\1\2", text)
|
||||
detail_match = re.search(
|
||||
r"(?:비디오 판독|합의 판정):\s*(.+?)\s*([가-힣]+)→([가-힣]+)\s*$",
|
||||
normalized,
|
||||
)
|
||||
|
||||
detail_text = detail_match.group(1).strip() if detail_match else text
|
||||
review_item = infer_review_item(detail_text)
|
||||
before_result = normalize_review_result_token(detail_match.group(2), review_item) if detail_match else None
|
||||
after_result = normalize_review_result_token(detail_match.group(3), review_item) if detail_match else None
|
||||
|
||||
return {
|
||||
"type": "video_review",
|
||||
"text": text,
|
||||
"requestInningLabel": (
|
||||
f"{inning_match.group(1)}{'초' if inning_match.group(2) == '초' else '말'}"
|
||||
if inning_match else None
|
||||
),
|
||||
"requestTeam": request_team_match.group(1) if request_team_match else None,
|
||||
"reviewItem": review_item,
|
||||
"beforeResult": before_result,
|
||||
"finalResult": after_result,
|
||||
"isSuccess": (
|
||||
"성공" if before_result and after_result and before_result != after_result
|
||||
else "실패"
|
||||
),
|
||||
"timing": "before_pitch" if "초구 전" in text else "after_pitch",
|
||||
}
|
||||
|
||||
|
||||
def normalize_review_event(review_event: dict[str, Any]) -> dict[str, Any]:
|
||||
"""판독 이벤트를 정규화
|
||||
|
||||
beforeResult/finalResult가 누락된 경우 텍스트에서 재파싱
|
||||
"""
|
||||
has_results = (
|
||||
review_event.get("beforeResult") is not None
|
||||
and review_event.get("finalResult") is not None
|
||||
)
|
||||
if review_event.get("requestInningLabel") and review_event.get("reviewItem") and has_results:
|
||||
return review_event
|
||||
|
||||
text = review_event.get("text") or ""
|
||||
parsed = parse_review_event_text(text)
|
||||
parsed.update({k: v for k, v in review_event.items() if k not in parsed})
|
||||
return parsed
|
||||
|
||||
|
||||
def get_review_result_group(review_item: str) -> dict[str, Any] | None:
|
||||
"""사이트에서 판독항목에 대응하는 결과 그룹 정보 반환"""
|
||||
groups = review_result_groups()
|
||||
return groups.get(review_item)
|
||||
Reference in New Issue
Block a user