добавлены Судьи, поправлены Ход игры и События игры на офлайн матч
This commit is contained in:
185
get_data_new.py
185
get_data_new.py
@@ -10,6 +10,7 @@ from datetime import datetime, timedelta, timezone
|
||||
from zoneinfo import ZoneInfo
|
||||
from typing import Any, Dict, List, Tuple, Optional
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
import requests
|
||||
from requests.adapters import HTTPAdapter
|
||||
@@ -549,7 +550,7 @@ def poll_game_live(
|
||||
if game_finished:
|
||||
break
|
||||
|
||||
time.sleep(0.2)
|
||||
time.sleep(1)
|
||||
|
||||
# вторая точка выхода по stop_event после sleep
|
||||
if stop_event.is_set():
|
||||
@@ -652,6 +653,8 @@ def render_loop(stop_event: threading.Event, out_name: str = "game") -> None:
|
||||
Json_Team_Generation(state, who="team1")
|
||||
Json_Team_Generation(state, who="team2")
|
||||
Scores_Quarter(state)
|
||||
Referee(state)
|
||||
Play_By_Play(state)
|
||||
|
||||
# live_status отдельно, + общий state в <out_name>.json
|
||||
atomic_write_json([state["result"]["live_status"]], "live_status")
|
||||
@@ -761,7 +764,7 @@ def Referee(merged: dict, *, out_dir: str = "static") -> None:
|
||||
else len(desired_order)
|
||||
),
|
||||
)
|
||||
out_path = "referee.json"
|
||||
out_path = "referee"
|
||||
atomic_write_json(referees, out_path)
|
||||
logging.info("Сохранил payload: {out_path}")
|
||||
|
||||
@@ -769,6 +772,172 @@ def Referee(merged: dict, *, out_dir: str = "static") -> None:
|
||||
logger.error(f"Ошибка в Referee потоке: {e}", exc_info=True)
|
||||
|
||||
|
||||
def Play_By_Play(data: dict) -> None:
|
||||
"""
|
||||
Поток, обновляющий JSON-файл с последовательностью бросков в матче.
|
||||
"""
|
||||
logger.info("START making json for play-by-play")
|
||||
|
||||
try:
|
||||
game_data = data["result"] if "result" in data else data
|
||||
|
||||
if not game_data:
|
||||
logger.debug("game_online_data отсутствует")
|
||||
return
|
||||
|
||||
teams = game_data["teams"]
|
||||
team1_data = next((i for i in teams if i.get("teamNumber") == 1), None)
|
||||
team2_data = next((i for i in teams if i.get("teamNumber") == 2), None)
|
||||
|
||||
if not team1_data or not team2_data:
|
||||
logger.warning("Не удалось получить команды из game_online_data")
|
||||
return
|
||||
team1_name = game_data["team1"]["name"]
|
||||
team2_name = game_data["team2"]["name"]
|
||||
team1_startnum = [
|
||||
p["startNum"]
|
||||
for p in team1_data.get("starts", [])
|
||||
if p.get("startRole") == "Player"
|
||||
]
|
||||
team2_startnum = [
|
||||
p["startNum"]
|
||||
for p in team2_data.get("starts", [])
|
||||
if p.get("startRole") == "Player"
|
||||
]
|
||||
|
||||
plays = game_data.get("plays", [])
|
||||
if not plays:
|
||||
logger.debug("нет данных в play-by-play")
|
||||
return
|
||||
|
||||
# Получение текущего времени игры
|
||||
json_live_status = data["result"]["live_status"] if "result" in data and "live_status" in data["result"] else None
|
||||
last_event = plays[-1]
|
||||
|
||||
# if not json_live_status or json_live_status.get("message") == "Not Found":
|
||||
if json_live_status is None:
|
||||
period = last_event.get("period", 1)
|
||||
second = 0
|
||||
else:
|
||||
period = (json_live_status or {}).get("result", {}).get("period", 1)
|
||||
second = (json_live_status or {}).get("result", {}).get("second", 0)
|
||||
|
||||
# Создание DataFrame из событий
|
||||
df = pd.DataFrame(plays[::-1])
|
||||
|
||||
# Преобразование для лиги 3x3
|
||||
# if "3x3" in LEAGUE:
|
||||
# df["play"].replace({2: 1, 3: 2}, inplace=True)
|
||||
|
||||
df_goals = df[df["play"].isin([1, 2, 3])].copy()
|
||||
if df_goals.empty:
|
||||
logger.debug("нет данных о голах в play-by-play")
|
||||
return
|
||||
|
||||
# Расчёты по очкам и времени
|
||||
df_goals["score1"] = (
|
||||
df_goals["startNum"].isin(team1_startnum) * df_goals["play"]
|
||||
)
|
||||
df_goals["score2"] = (
|
||||
df_goals["startNum"].isin(team2_startnum) * df_goals["play"]
|
||||
)
|
||||
|
||||
df_goals["score_sum1"] = df_goals["score1"].fillna(0).cumsum()
|
||||
df_goals["score_sum2"] = df_goals["score2"].fillna(0).cumsum()
|
||||
|
||||
df_goals["new_sec"] = (
|
||||
pd.to_numeric(df_goals["sec"], errors="coerce").fillna(0).astype(int)
|
||||
// 10
|
||||
)
|
||||
df_goals["time_now"] = (600 if period < 5 else 300) - second
|
||||
df_goals["quar"] = period - df_goals["period"]
|
||||
|
||||
df_goals["diff_time"] = np.where(
|
||||
df_goals["quar"] == 0,
|
||||
df_goals["time_now"] - df_goals["new_sec"],
|
||||
(600 * df_goals["quar"] - df_goals["new_sec"]) + df_goals["time_now"],
|
||||
)
|
||||
|
||||
df_goals["diff_time_str"] = df_goals["diff_time"].apply(
|
||||
lambda x: (
|
||||
f"{x // 60}:{str(x % 60).zfill(2)}" if isinstance(x, int) else x
|
||||
)
|
||||
)
|
||||
|
||||
# Текстовые поля
|
||||
def generate_text(row, with_time=False, is_rus=False):
|
||||
s1, s2 = int(row["score_sum1"]), int(row["score_sum2"])
|
||||
team = (
|
||||
team1_name
|
||||
if not pd.isna(row["score1"]) and row["score1"] != 0
|
||||
else team2_name
|
||||
)
|
||||
|
||||
# ✅ Правильный порядок счёта в зависимости от команды
|
||||
if team == team1_name:
|
||||
score = f"{s1}-{s2}"
|
||||
else:
|
||||
score = f"{s2}-{s1}"
|
||||
|
||||
time_str = (
|
||||
f" за {row['diff_time_str']}"
|
||||
if is_rus
|
||||
else f" in last {row['diff_time_str']}"
|
||||
)
|
||||
prefix = "рывок" if is_rus else "run"
|
||||
|
||||
return f"{team} {score} {prefix}{time_str if with_time else ''}"
|
||||
|
||||
df_goals["text_rus"] = df_goals.apply(
|
||||
lambda r: generate_text(r, is_rus=True, with_time=False), axis=1
|
||||
)
|
||||
df_goals["text_time_rus"] = df_goals.apply(
|
||||
lambda r: generate_text(r, is_rus=True, with_time=True), axis=1
|
||||
)
|
||||
df_goals["text"] = df_goals.apply(
|
||||
lambda r: generate_text(r, is_rus=False, with_time=False), axis=1
|
||||
)
|
||||
df_goals["text_time"] = df_goals.apply(
|
||||
lambda r: generate_text(r, is_rus=False, with_time=True), axis=1
|
||||
)
|
||||
|
||||
df_goals["team"] = df_goals["score1"].apply(
|
||||
lambda x: team1_name if not pd.isna(x) and x != 0 else team2_name
|
||||
)
|
||||
|
||||
# Удаление лишнего
|
||||
drop_cols = [
|
||||
"children",
|
||||
"start",
|
||||
"stop",
|
||||
"hl",
|
||||
"sort",
|
||||
"startNum",
|
||||
"zone",
|
||||
"x",
|
||||
"y",
|
||||
]
|
||||
df_goals.drop(columns=drop_cols, inplace=True, errors="ignore")
|
||||
|
||||
# Порядок колонок
|
||||
main_cols = ["text", "text_time"]
|
||||
all_cols = main_cols + [
|
||||
col for col in df_goals.columns if col not in main_cols
|
||||
]
|
||||
df_goals = df_goals[all_cols]
|
||||
|
||||
# Сохранение JSON
|
||||
directory = "static"
|
||||
os.makedirs(directory, exist_ok=True)
|
||||
filepath = os.path.join(directory, "play_by_play.json")
|
||||
|
||||
df_goals.to_json(filepath, orient="records", force_ascii=False, indent=4)
|
||||
logger.debug("Успешно положил данные об play-by-play в файл")
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка в Play_By_Play: {e}", exc_info=True)
|
||||
|
||||
|
||||
|
||||
def render_once_after_game(
|
||||
session: requests.Session,
|
||||
league: str,
|
||||
@@ -808,6 +977,7 @@ def render_once_after_game(
|
||||
Json_Team_Generation(state, who="team2")
|
||||
Scores_Quarter(state)
|
||||
Referee(state)
|
||||
Play_By_Play(state)
|
||||
|
||||
# === 4. live_status и общий state ===
|
||||
atomic_write_json(state["result"], out_name)
|
||||
@@ -817,6 +987,7 @@ def render_once_after_game(
|
||||
except Exception as ex:
|
||||
logger.exception(f"[RENDER_ONCE] error while building final state: {ex}")
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# 7. Постобработка статистики для вывода
|
||||
# ============================================================================
|
||||
@@ -1446,7 +1617,9 @@ def Standing_func(
|
||||
|
||||
# когда мы последний раз успешно обновили standings
|
||||
last_call_ts = 0
|
||||
json_seasons = fetch_api_data(session, "seasons", host=HOST, league=league, lang=lang)
|
||||
json_seasons = fetch_api_data(
|
||||
session, "seasons", host=HOST, league=league, lang=lang
|
||||
)
|
||||
season = json_seasons[0]["season"]
|
||||
|
||||
# как часто вообще можно дёргать standings
|
||||
@@ -1549,7 +1722,7 @@ def Standing_func(
|
||||
filename = f"standings_{league}_{comp_name}"
|
||||
atomic_write_json(standings_payload, filename, out_dir)
|
||||
logger.info(
|
||||
f"[STANDINGS_THREAD] saved {filename}.json ({len(standings_payload)} rows)"
|
||||
f"[STANDINGS_THREAD] сохранил {filename}.json"
|
||||
)
|
||||
|
||||
# 2) плейофф-пары (playoffPairs)
|
||||
@@ -1757,8 +1930,8 @@ def main():
|
||||
standings_thread.join()
|
||||
logger.info("[MAIN] standings thread stopped, shutdown complete")
|
||||
|
||||
# идём на новую итерацию while True
|
||||
# (новая сессия / новый stop_event создаются в начале цикла)
|
||||
# идём на новую итерацию while True
|
||||
# (новая сессия / новый stop_event создаются в начале цикла)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
|
||||
Reference in New Issue
Block a user