diff --git a/get_data.py b/get_data.py index 7e48e7d..c05c88c 100644 --- a/get_data.py +++ b/get_data.py @@ -17,6 +17,7 @@ from fastapi.responses import Response import logging import logging.config import platform +import socket # передадим параметры через аргументы или глобальные переменные @@ -28,6 +29,7 @@ parser.add_argument("--lang", default="en") args = parser.parse_args() MYHOST = platform.node() +user_name = socket.gethostname() if not os.path.exists("logs"): os.makedirs("logs") @@ -65,7 +67,7 @@ log_config = { "formatters": { "telegram": { "class": "telegram_handler.HtmlFormatter", - "format": f"%(levelname)s [{MYHOST.upper()}] %(message)s", + "format": f"%(levelname)s [{MYHOST.upper()}] [{user_name}]\n%(message)s", "use_emoji": "True", }, "simple": { @@ -225,6 +227,7 @@ def start_live_threads(season, game_id): URLS["game"].format(host=HOST, game_id=game_id, lang=LANG), 300, # часто stop_event_live, + True, ), daemon=True, ), @@ -304,13 +307,17 @@ def stop_offline_threads(): # Функция запускаемая в потоках def get_data_from_API( - name: str, url: str, sleep_time: float, stop_event: threading.Event + name: str, url: str, sleep_time: float, stop_event: threading.Event, stop_when_live=False ): - + did_first_fetch = False while not stop_event.is_set(): + if stop_when_live and globals().get("STATUS") == "live" and did_first_fetch: + logger.info(f"[{name}] stopping because STATUS='live' and first fetch done") + break start = time.time() try: value = requests.get(url, timeout=5).json() + did_first_fetch = True # помечаем, что один заход сделали except json.JSONDecodeError as json_err: logger.warning(f"[{name}] Ошибка парсинга JSON: {json_err}") value = {"error": f"JSON decode error: {json_err}"} @@ -342,7 +349,16 @@ def get_data_from_API( # to_sleep = sleep_time - elapsed # print(to_sleep) # if to_sleep > 0: - time.sleep(sleep_time) + # умное ожидание с быстрым выходом при live + slept = 0 + while slept < sleep_time: + if stop_event.is_set(): + break + if stop_when_live and globals().get("STATUS") == "live" and did_first_fetch: + logger.info(f"[{name}] stopping during sleep because STATUS='live' and first fetch done") + return + time.sleep(1) + slept += 1 # если запрос занял дольше — просто сразу следующую итерацию @@ -370,7 +386,15 @@ def results_consumer(): # универсальный статус (может не быть) incoming_status = payload.get("status") # может быть None - + # print(source, incoming_status) + if source == "game": + # обработка ТОЛЬКО в спец-ветке ниже + pass + else: + latest_data[source] = { + "ts": msg["ts"], + "data": incoming_status if incoming_status is not None else payload, + } # 1) play-by-play if "play-by-play" in source: game = latest_data.get("game") @@ -386,10 +410,6 @@ def results_consumer(): game["data"]["result"]["plays"] = payload["result"] # а вот статус у play-by-play иногда просто "no-status" - latest_data[source] = { - "ts": msg["ts"], - "data": incoming_status if incoming_status is not None else payload, - } # 2) box-score elif "box-score" in source: @@ -438,10 +458,10 @@ def results_consumer(): team["maxPointsInRow"] = box_team["maxPointsInRow"] # в любом случае сохраняем сам факт, что box-score пришёл - latest_data[source] = { - "ts": msg["ts"], - "data": incoming_status if incoming_status is not None else payload, - } + # latest_data[source] = { + # "ts": msg["ts"], + # "data": incoming_status if incoming_status is not None else payload, + # } elif "live-status" in source: latest_data[source] = { @@ -512,10 +532,8 @@ def results_consumer(): ) globals()["OFFLINE_SWITCH_AT"] = None - if globals().get("STATUS") not in ["live", "live_soon"]: - logger.info( - "[status] match became LIVE → switch to LIVE threads" - ) + if globals().get("STATUS") != "live": + logger.info("[status] match became LIVE → switch to LIVE threads") globals()["STATUS"] = "live" start_live_threads(SEASON, GAME_ID) @@ -526,7 +544,8 @@ def results_consumer(): else: if source == "game": - has_game_already = "game" in latest_data + # has_game_already = "teams" in latest_data + has_game_already = "game" in latest_data and isinstance(latest_data.get("game"), dict) # есть ли в ответе ПОЛНАЯ структура is_full = ( @@ -542,18 +561,26 @@ def results_consumer(): "data": payload, } else: - # game неполный - if not has_game_already: - # 👉 раньше game вообще не было — лучше положить хоть что-то - latest_data["game"] = { - "ts": msg["ts"], - "data": payload, - } + if globals().get("STATUS") in ("live", "live_soon") and has_game_already: + logger.debug("results_consumer: LIVE & partial game → keep previous one") else: - # 👉 уже есть какой-то game — неполным НЕ затираем - logger.debug( - "results_consumer: got partial game, keeping previous one" - ) + if not has_game_already: + # раньше не было вообще — положим хоть что-то + latest_data["game"] = {"ts": msg["ts"], "data": payload} + else: + logger.debug("results_consumer: got partial game, keeping previous one") + # # game неполный + # if not has_game_already: + # # 👉 раньше game вообще не было — лучше положить хоть что-то + # latest_data["game"] = { + # "ts": msg["ts"], + # "data": payload, + # } + # else: + # # 👉 уже есть какой-то game — неполным НЕ затираем + # logger.debug( + # "results_consumer: got partial game, keeping previous one" + # ) # и обязательно continue/return из этого elif/if else: @@ -666,7 +693,7 @@ def build_pretty_status_message(): Если game ещё нет — шлём хотя бы статусы источников. """ lines = [] - lines.append(f"\n🏀 {LEAGUE.upper()} • {TEAM}") + lines.append(f"🏀 {LEAGUE.upper()} • {TEAM}") lines.append(f"📌 Game ID: {GAME_ID}") lines.append(f"🕒 {GAME_START_DT}") @@ -674,17 +701,27 @@ def build_pretty_status_message(): game_wrap = latest_data.get("game") has_game = False if game_wrap: - game_data = game_wrap.get("data") or game_wrap - result = game_data.get("result") or {} + raw = game_wrap.get("data") if isinstance(game_wrap, dict) else game_wrap + + # raw может быть: dict (полный payload) | dict (уже result) | str ("ok"/"no-status") + result = {} + if isinstance(raw, dict): + # ваш нормальный полный ответ по game имеет структуру: {"data": {"result": {...}}} + # но на всякий случай поддержим и вариант, где сразу {"result": {...}} или уже {"game": ...} + result = raw.get("data", {}).get("result", {}) if "data" in raw else (raw.get("result") or raw) + else: + result = {} + game_info = result.get("game") or {} - team1_name = result["team1"]["name"] - team2_name = result["team2"]["name"] + team1_name = (result.get("team1") or {}).get("name", "Team 1") + team2_name = (result.get("team2") or {}).get("name", "Team 2") + + lines.append(f"👥 {team1_name} vs {team2_name}") score_now = game_info.get("score") or "" full_score = game_info.get("fullScore") or "" - lines.append(f"👥 {team1_name} vs {team2_name}") if score_now: lines.append(f"🔢 Score: {score_now}") @@ -694,14 +731,31 @@ def build_pretty_status_message(): if q_text: lines.append(f"🧱 By quarters: {q_text}") - has_game = True - + has_game = bool(result) # live-status отдельно - ls = latest_data.get("live-status", {}) - ls_raw = ls.get("data") or {} - ls_status = ( - ls_raw.get("status") or ls_raw.get("gameStatus") or ls_raw.get("state") or "—" - ) + # ls = latest_data.get("live-status", {}) + # ls_raw = ls.get("data") or {} + # ls_status = ( + # ls_raw.get("status") or ls_raw.get("gameStatus") or ls_raw.get("state") or "—" + # ) + # lines.append(f"🟢 LIVE status: {ls_status}") + + ls_wrap = latest_data.get("live-status") + ls_status = "—" + if ls_wrap: + raw = ls_wrap.get("data") + if isinstance(raw, dict): + ls_dict = raw.get("result") or raw + ls_status = ( + ls_dict.get("status") + or ls_dict.get("gameStatus") + or ls_dict.get("state") + or "—" + ) + elif isinstance(raw, str): + # API/consumer могли положить просто строку статуса: "ok", "no-status", "error" + ls_status = raw + lines.append(f"🟢 LIVE status: {ls_status}") # добавим блок по источникам — это как раз “состояние запросов” @@ -864,98 +918,6 @@ async def lifespan(app: FastAPI): ) thread_status_broadcaster.start() - # 5. Подготовим онлайн и офлайн наборы (как у тебя) - threads_live = [ - threading.Thread( - target=get_data_from_API, - args=( - "pregame", - URLS["pregame"].format( - host=HOST, league=LEAGUE, season=season, game_id=game_id, lang=LANG - ), - 600, - stop_event, - ), - daemon=True, - ), - threading.Thread( - target=get_data_from_API, - args=( - "pregame-full-stats", - URLS["pregame-full-stats"].format( - host=HOST, league=LEAGUE, season=season, game_id=game_id, lang=LANG - ), - 600, - stop_event, - ), - daemon=True, - ), - threading.Thread( - target=get_data_from_API, - args=( - "actual-standings", - URLS["actual-standings"].format( - host=HOST, league=LEAGUE, season=season, lang=LANG - ), - 600, - stop_event, - ), - daemon=True, - ), - threading.Thread( - target=get_data_from_API, - args=( - "game", - URLS["game"].format(host=HOST, game_id=game_id, lang=LANG), - 600, - stop_event, - ), - daemon=True, - ), - threading.Thread( - target=get_data_from_API, - args=( - "live-status", - URLS["live-status"].format(host=HOST, game_id=game_id), - 0.5, - stop_event, - ), - daemon=True, - ), - threading.Thread( - target=get_data_from_API, - args=( - "box-score", - URLS["box-score"].format(host=HOST, game_id=game_id), - 0.5, - stop_event, - ), - daemon=True, - ), - threading.Thread( - target=get_data_from_API, - args=( - "play-by-play", - URLS["play-by-play"].format(host=HOST, game_id=game_id), - 1, - stop_event, - ), - daemon=True, - ), - ] - threads_offline = [ - threading.Thread( - target=get_data_from_API, - args=( - "game", - URLS["game"].format(host=HOST, game_id=game_id, lang=LANG), - 600, # реже - stop_event, - ), - daemon=True, - ) - ] - # 5. решаем, что запускать if not is_today: STATUS = "no_game_today" @@ -1141,7 +1103,8 @@ async def status(request: Request): accept = request.headers.get("accept", "") if "text/html" in accept: status_raw = str(STATUS).lower() - if status_raw in ["live"]: + # print(status_raw) + if status_raw in ["live", "online"]: gs_class = "live" gs_text = "🟢 LIVE" elif status_raw in ["live_soon", "today_not_started"]: @@ -1968,7 +1931,7 @@ async def team_comparison(): async def regular_standings(): data = latest_data["actual-standings"]["data"]["items"] for item in data: - if item["comp"]["name"] == "Regular Season": + # if item["comp"]["name"] == "Regular Season": if item.get("standings"): standings_rows = item["standings"]