"""경기 일정 SSOT — lib/schedule.ts(Group A 정식 일정, KST) 와 동일. 투표 오픈 = 킥오프 - settings.vote_open_hours_before (D-2 = 48h) 투표 마감 = 킥오프 - settings.vote_lock_minutes_before (기본 0 = 킥오프 정각) """ from __future__ import annotations from dataclasses import dataclass from datetime import datetime, timedelta, timezone from .config import settings # 팀 정의 (코드 → 표시명/약식/이모지) TEAMS = { "KOR": {"name": "Korea Republic", "shortName": "한국", "code": "KOR", "flag": "🇰🇷"}, "CZE": {"name": "Czechia", "shortName": "체코", "code": "CZE", "flag": "🇨🇿"}, "MEX": {"name": "Mexico", "shortName": "멕시코", "code": "MEX", "flag": "🇲🇽"}, "RSA": {"name": "South Africa", "shortName": "남아공", "code": "RSA", "flag": "🇿🇦"}, } # 외부 소스의 팀 표기 → 내부 코드 매핑 (openfootball / football-data 등 변형 포괄). NAME_TO_CODE = { "korea republic": "KOR", "south korea": "KOR", "korea": "KOR", "czechia": "CZE", "czech republic": "CZE", "mexico": "MEX", "south africa": "RSA", } def code_for(name: str) -> str | None: return NAME_TO_CODE.get(name.strip().lower()) @dataclass(frozen=True) class MatchSeed: match_id: str round_label: str team_a: str # 코드 team_b: str kickoff_iso: str # ISO with +09:00 venue: str hook_text: str # Group A 전체 6경기 (출처: lib/schedule.ts) GROUP_A: list[MatchSeed] = [ MatchSeed( "A_MEX_RSA_20260612", "Match 01 · 개막전", "MEX", "RSA", "2026-06-12T04:00:00+09:00", "Estadio Azteca · Mexico City", "글로벌 축구 개막전, AI는 개최국을 믿을까", ), MatchSeed( "A_KOR_CZE_20260612", "Match 01", "KOR", "CZE", "2026-06-12T11:00:00+09:00", "Estadio Guadalajara (Akron)", "한국 첫 경기, AI의 선택은 갈렸다", ), MatchSeed( "A_CZE_RSA_20260619", "Match 02", "CZE", "RSA", "2026-06-19T01:00:00+09:00", "USA (TBD)", "체코 vs 남아공, AI의 예측은", ), MatchSeed( "A_MEX_KOR_20260619", "Match 02", "MEX", "KOR", "2026-06-19T10:00:00+09:00", "Estadio Guadalajara (Akron)", "개최국 멕시코 vs 한국, AI는 누구 편", ), MatchSeed( "A_CZE_MEX_20260625", "Match 03", "CZE", "MEX", "2026-06-25T10:00:00+09:00", "Estadio Azteca · Mexico City", "체코 vs 멕시코, 운명의 최종전", ), MatchSeed( "A_RSA_KOR_20260625", "Match 03", "RSA", "KOR", "2026-06-25T10:00:00+09:00", "Estadio BBVA · Monterrey", "한국 16강의 갈림길, AI의 마지막 예측", ), ] FEATURED_MATCH_ID = "A_KOR_CZE_20260612" def parse_kickoff(iso: str) -> datetime: # 저장은 UTC 로 통일 (DB 종류 무관하게 절대시점 일관). 출력 시 KST 로 직렬화. return datetime.fromisoformat(iso).astimezone(timezone.utc) def opens_at(kickoff: datetime) -> datetime: return kickoff - timedelta(hours=settings.vote_open_hours_before) def lock_at(kickoff: datetime) -> datetime: return kickoff - timedelta(minutes=settings.vote_lock_minutes_before)