в процессе логирования

This commit is contained in:
2025-11-01 11:48:32 +03:00
parent 02479f8046
commit 6ffb7df0f7

View File

@@ -9,13 +9,14 @@ import time
import queue import queue
import argparse import argparse
import uvicorn import uvicorn
from pprint import pprint
import os import os
import pandas as pd import pandas as pd
import json import json
from datetime import datetime, time as dtime, timedelta from datetime import datetime, time as dtime, timedelta
from fastapi.responses import Response from fastapi.responses import Response
import logging
import logging.config
import platform
# передадим параметры через аргументы или глобальные переменные # передадим параметры через аргументы или глобальные переменные
@@ -26,16 +27,72 @@ parser.add_argument("--team", required=True)
parser.add_argument("--lang", default="en") parser.add_argument("--lang", default="en")
args = parser.parse_args() args = parser.parse_args()
MYHOST = platform.node()
if not os.path.exists("logs"):
os.makedirs("logs")
telegram_bot_token = "7639240596:AAH0YtdQoWZSC-_R_EW4wKAHHNLIA0F_ARY"
# TELEGRAM_CHAT_ID = 228977654
telegram_chat_id = -4803699526
log_config = {
"version": 1,
"handlers": {
"telegram": {
"class": "telegram_handler.TelegramHandler",
"level": "INFO",
"token": telegram_bot_token,
"chat_id": telegram_chat_id,
"formatter": "telegram",
},
"console": {
"class": "logging.StreamHandler",
"level": "INFO",
"formatter": "simple",
"stream": "ext://sys.stdout",
},
"file": {
"class": "logging.FileHandler",
"level": "DEBUG",
"formatter": "simple",
"filename": f"logs/GFX_{MYHOST}.log",
"encoding": "utf-8",
},
},
"loggers": {
__name__: {"handlers": ["console", "file", "telegram"], "level": "DEBUG"},
},
"formatters": {
"telegram": {
"class": "telegram_handler.HtmlFormatter",
"format": f"%(levelname)s [{MYHOST.upper()}] %(message)s",
"use_emoji": "True",
},
"simple": {
"class": "logging.Formatter",
"format": "%(asctime)s %(levelname)-8s %(funcName)s() - %(message)s",
"datefmt": "%d.%m.%Y %H:%M:%S",
},
},
}
logging.config.dictConfig(log_config)
logger = logging.getLogger(__name__)
logger.handlers[2].formatter.use_emoji = True
LEAGUE = args.league LEAGUE = args.league
TEAM = args.team TEAM = args.team
LANG = args.lang LANG = args.lang
HOST = "https://pro.russiabasket.org" HOST = "https://deti.russiabasket.org"
STATUS = False STATUS = False
GAME_ID = None GAME_ID = None
SEASON = None SEASON = None
GAME_START_DT = None # datetime начала матча (локальная из календаря) GAME_START_DT = None # datetime начала матча (локальная из календаря)
GAME_TODAY = False # флаг: игра сегодня GAME_TODAY = False # флаг: игра сегодня
GAME_SOON = False # флаг: игра сегодня и скоро (<1 часа) GAME_SOON = False # флаг: игра сегодня и скоро (<1 часа)
URLS = { URLS = {
"seasons": "{host}/api/abc/comps/seasons?Tag={league}", "seasons": "{host}/api/abc/comps/seasons?Tag={league}",
@@ -50,6 +107,158 @@ URLS = {
} }
def start_offline_threads(season, game_id):
"""Запускаем редкие запросы, когда матча нет или он уже сыгран."""
global threads_offline, CURRENT_THREADS_MODE, stop_event_offline
# если уже работаем в офлайне — не дублируем
if CURRENT_THREADS_MODE == "offline":
return
# на всякий случай гасим лайв
stop_live_threads()
stop_event_offline.clear()
threads_offline = [
threading.Thread(
target=get_data_from_API,
args=(
"game",
URLS["game"].format(host=HOST, game_id=game_id, lang=LANG),
1, # раз в секунду/реже
stop_event_offline,
),
daemon=True,
)
]
for t in threads_offline:
t.start()
CURRENT_THREADS_MODE = "offline"
print("[threads] OFFLINE threads started")
def start_live_threads(season, game_id):
"""Запускаем частые онлайн-запросы, когда матч идёт/вот-вот."""
global threads_live, CURRENT_THREADS_MODE, stop_event_live
# если уже в лайве — не дублируем
if CURRENT_THREADS_MODE == "live":
return
# на всякий случай гасим офлайн
stop_offline_threads()
stop_event_live.clear()
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
),
0.0016667,
stop_event_live,
),
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
),
0.0016667,
stop_event_live,
),
daemon=True,
),
threading.Thread(
target=get_data_from_API,
args=(
"actual-standings",
URLS["actual-standings"].format(
host=HOST, league=LEAGUE, season=season, lang=LANG
),
0.0016667,
stop_event_live,
),
daemon=True,
),
threading.Thread(
target=get_data_from_API,
args=(
"game",
URLS["game"].format(host=HOST, game_id=game_id, lang=LANG),
0.00016, # часто
stop_event_live,
),
daemon=True,
),
threading.Thread(
target=get_data_from_API,
args=(
"live-status",
URLS["live-status"].format(host=HOST, game_id=game_id),
1,
stop_event_live,
),
daemon=True,
),
threading.Thread(
target=get_data_from_API,
args=(
"box-score",
URLS["box-score"].format(host=HOST, game_id=game_id),
1,
stop_event_live,
),
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_live,
),
daemon=True,
),
]
for t in threads_live:
t.start()
CURRENT_THREADS_MODE = "live"
print("[threads] LIVE threads started")
def stop_live_threads():
"""Гасим только live-треды."""
global threads_live
if not threads_live:
return
stop_event_live.set()
for t in threads_live:
t.join(timeout=1)
threads_live = []
print("[threads] LIVE threads stopped")
def stop_offline_threads():
"""Гасим только offline-треды."""
global threads_offline
if not threads_offline:
return
stop_event_offline.set()
for t in threads_offline:
t.join(timeout=1)
threads_offline = []
print("[threads] OFFLINE threads stopped")
# общая очередь # общая очередь
results_q = queue.Queue() results_q = queue.Queue()
# тут будем хранить последние данные # тут будем хранить последние данные
@@ -57,6 +266,17 @@ latest_data = {}
# событие для остановки потоков # событие для остановки потоков
stop_event = threading.Event() stop_event = threading.Event()
# отдельные события для разных наборов потоков
stop_event_live = threading.Event()
stop_event_offline = threading.Event()
# чтобы из consumer можно было их гасить
threads_live = []
threads_offline = []
# какой режим сейчас запущен: "live" / "offline" / None
CURRENT_THREADS_MODE = None
# Функция запускаемая в потоках # Функция запускаемая в потоках
def get_data_from_API( def get_data_from_API(
@@ -133,7 +353,9 @@ def results_consumer():
and "teams" in payload["result"] and "teams" in payload["result"]
): ):
# обновляем команды # обновляем команды
game["data"]["result"]["game"]["fullScore"] = payload["result"]["fullScore"] game["data"]["result"]["game"]["fullScore"] = payload["result"][
"fullScore"
]
for team in game["data"]["result"]["teams"]: for team in game["data"]["result"]["teams"]:
if team["teamNumber"] != 0: if team["teamNumber"] != 0:
box_team = [ box_team = [
@@ -169,17 +391,13 @@ def results_consumer():
} }
elif "live-status" in source: elif "live-status" in source:
# просто сохраним, как и остальные
latest_data[source] = { latest_data[source] = {
"ts": msg["ts"], "ts": msg["ts"],
"data": payload, "data": payload,
} }
# попытка ДОПОЛНИТЕЛЬНО обновить глобальный STATUS по live-status
try: try:
ls_data = payload.get("result") or payload # иногда сразу result ls_data = payload.get("result") or payload
# тут нужно посмотреть, какое именно поле у тебя в live-status
# допустим, там есть что-то вроде "status" или "gameStatus"
raw_ls_status = ( raw_ls_status = (
ls_data.get("status") ls_data.get("status")
or ls_data.get("gameStatus") or ls_data.get("gameStatus")
@@ -187,27 +405,42 @@ def results_consumer():
) )
if raw_ls_status: if raw_ls_status:
raw_ls_status = str(raw_ls_status).lower() raw_ls_status_low = str(raw_ls_status).lower()
# варианты, которые считаем "матч окончен"
finished_markers = [ finished_markers = [
"finished", "finished",
"result", "result",
"resultconfirmed", "resultconfirmed",
"ended", "ended",
"game over",
"final", "final",
"game over",
] ]
if any(m in raw_ls_status for m in finished_markers): # матч ЗАКОНЧЕН → гасим live и включаем offline
# перезатираем глобальный статус — он более актуальный if any(m in raw_ls_status_low for m in finished_markers):
# если матч сегодня — делаем finished_today, иначе просто finished print("[status] match finished → switch to OFFLINE")
from datetime import datetime if (
GAME_START_DT
if GAME_START_DT and GAME_START_DT.date() == datetime.now().date(): and GAME_START_DT.date() == datetime.now().date()
):
globals()["STATUS"] = "finished_today" globals()["STATUS"] = "finished_today"
else: else:
globals()["STATUS"] = "finished" globals()["STATUS"] = "finished"
stop_live_threads()
start_offline_threads(SEASON, GAME_ID)
# матч СТАЛ онлайном (напр., из Scheduled → Online)
elif (
"online" in raw_ls_status_low or "live" in raw_ls_status_low
):
if globals().get("STATUS") not in ["live", "live_soon"]:
print(
"[status] match became LIVE → switch to LIVE threads"
)
globals()["STATUS"] = "live"
start_live_threads(SEASON, GAME_ID)
except Exception as e: except Exception as e:
print("results_consumer: live-status postprocess error:", e) print("results_consumer: live-status postprocess error:", e)
@@ -238,7 +471,9 @@ def results_consumer():
} }
else: else:
# 👉 уже есть какой-то game — неполным НЕ затираем # 👉 уже есть какой-то game — неполным НЕ затираем
print("results_consumer: got partial game, keeping previous one") print(
"results_consumer: got partial game, keeping previous one"
)
# и обязательно continue/return из этого elif/if # и обязательно continue/return из этого elif/if
else: else:
@@ -253,6 +488,7 @@ def results_consumer():
print("results_consumer error:", repr(e)) print("results_consumer error:", repr(e))
continue continue
def get_items(data: dict) -> list: def get_items(data: dict) -> list:
""" """
Мелкий хелпер: берём первый список в ответе API. Мелкий хелпер: берём первый список в ответе API.
@@ -268,6 +504,7 @@ def get_items(data: dict) -> list:
from datetime import datetime from datetime import datetime
def pick_game_for_team(calendar_json): def pick_game_for_team(calendar_json):
""" """
Возвращает: Возвращает:
@@ -292,7 +529,11 @@ def pick_game_for_team(calendar_json):
continue continue
gdt = extract_game_datetime(game) gdt = extract_game_datetime(game)
gdate = gdt.date() if gdt else datetime.strptime(game["game"]["localDate"], "%d.%m.%Y").date() gdate = (
gdt.date()
if gdt
else datetime.strptime(game["game"]["localDate"], "%d.%m.%Y").date()
)
if gdate == today: if gdate == today:
cal_status = game["game"].get("gameStatus") cal_status = game["game"].get("gameStatus")
@@ -308,7 +549,11 @@ def pick_game_for_team(calendar_json):
continue continue
gdt = extract_game_datetime(game) gdt = extract_game_datetime(game)
gdate = gdt.date() if gdt else datetime.strptime(game["game"]["localDate"], "%d.%m.%Y").date() gdate = (
gdt.date()
if gdt
else datetime.strptime(game["game"]["localDate"], "%d.%m.%Y").date()
)
if gdate <= today: if gdate <= today:
last_id = game["game"]["id"] last_id = game["game"]["id"]
@@ -319,7 +564,6 @@ def pick_game_for_team(calendar_json):
return last_id, last_dt, False, last_status return last_id, last_dt, False, last_status
def extract_game_datetime(game_item: dict) -> datetime | None: def extract_game_datetime(game_item: dict) -> datetime | None:
""" """
Из элемента календаря достаём datetime матча. Из элемента календаря достаём datetime матча.
@@ -336,7 +580,7 @@ def extract_game_datetime(game_item: dict) -> datetime | None:
dt_time = dtime(hour=0, minute=0) dt_time = dtime(hour=0, minute=0)
return datetime.combine(dt_date, dt_time) return datetime.combine(dt_date, dt_time)
except Exception: except Exception:
return None return None
@asynccontextmanager @asynccontextmanager
@@ -345,7 +589,9 @@ async def lifespan(app: FastAPI):
# 1. проверяем API: seasons # 1. проверяем API: seasons
try: try:
seasons_resp = requests.get(URLS["seasons"].format(host=HOST, league=LEAGUE)).json() seasons_resp = requests.get(
URLS["seasons"].format(host=HOST, league=LEAGUE)
).json()
season = seasons_resp["items"][0]["season"] season = seasons_resp["items"][0]["season"]
except Exception: except Exception:
now = datetime.now() now = datetime.now()
@@ -367,7 +613,9 @@ async def lifespan(app: FastAPI):
calendar = None calendar = None
# 3. определяем игру # 3. определяем игру
game_id, game_dt, is_today, cal_status = pick_game_for_team(calendar) if calendar else (None, None, False, None) game_id, game_dt, is_today, cal_status = (
pick_game_for_team(calendar) if calendar else (None, None, False, None)
)
GAME_ID = game_id GAME_ID = game_id
GAME_START_DT = game_dt GAME_START_DT = game_dt
GAME_TODAY = is_today GAME_TODAY = is_today
@@ -381,7 +629,7 @@ async def lifespan(app: FastAPI):
# 5. Подготовим онлайн и офлайн наборы (как у тебя) # 5. Подготовим онлайн и офлайн наборы (как у тебя)
threads_live = [ threads_live = [
threading.Thread( threading.Thread(
target=get_data_from_API, target=get_data_from_API,
args=( args=(
"pregame", "pregame",
@@ -417,7 +665,6 @@ async def lifespan(app: FastAPI):
), ),
daemon=True, daemon=True,
), ),
threading.Thread( threading.Thread(
target=get_data_from_API, target=get_data_from_API,
args=( args=(
@@ -472,66 +719,43 @@ async def lifespan(app: FastAPI):
) )
] ]
# 6. Решение: сегодня / не сегодня # 5. решаем, что запускать
if not is_today: if not is_today:
# ИГРЫ СЕГОДНЯ НЕТ → крутим только офлайн
STATUS = "no_game_today" STATUS = "no_game_today"
for t in threads_offline: start_offline_threads(SEASON, GAME_ID)
t.start()
else: else:
# игра сегодня # игра сегодня
if cal_status is None: if cal_status is None:
# нет статуса в календаре — считаем, что ещё не началась
STATUS = "today_not_started" STATUS = "today_not_started"
for t in threads_offline: start_offline_threads(SEASON, GAME_ID)
t.start()
elif cal_status == "Scheduled": elif cal_status == "Scheduled":
# ещё не началась
# проверим, не меньше ли часа до начала
if game_dt: if game_dt:
delta = game_dt - datetime.now() delta = game_dt - datetime.now()
if delta <= timedelta(hours=1): if delta <= timedelta(hours=1):
# уже скоро → можно запускать онлайн
STATUS = "live_soon" STATUS = "live_soon"
GAME_SOON = True start_live_threads(SEASON, GAME_ID)
for t in threads_live:
t.start()
else: else:
# ещё далеко → офлайн, но говорим что сегодня
STATUS = "today_not_started" STATUS = "today_not_started"
for t in threads_offline: start_offline_threads(SEASON, GAME_ID)
t.start()
# и можно повесить будильник, как раньше
else: else:
# нет времени — просто офлайн, но сегодня
STATUS = "today_not_started" STATUS = "today_not_started"
for t in threads_offline: start_offline_threads(SEASON, GAME_ID)
t.start()
elif cal_status == "Online": elif cal_status == "Online":
# матч идёт → сразу онлайн
STATUS = "live" STATUS = "live"
GAME_SOON = False start_live_threads(SEASON, GAME_ID)
for t in threads_live:
t.start()
elif cal_status in ["Result", "ResultConfirmed"]: elif cal_status in ["Result", "ResultConfirmed"]:
# матч уже сыгран, но дата всё ещё сегодня
STATUS = "finished_today" STATUS = "finished_today"
for t in threads_offline: start_offline_threads(SEASON, GAME_ID)
t.start()
else: else:
# неизвестный статус — безопасный вариант
STATUS = "today_not_started" STATUS = "today_not_started"
for t in threads_offline: start_offline_threads(SEASON, GAME_ID)
t.start()
yield yield
# -------- shutdown -------- # -------- shutdown --------
stop_event.set() stop_event.set() # завершить consumer
# офлайн/онлайн ты можешь не делить тут, но оставлю stop_live_threads()
stop_event.set() stop_offline_threads()
for t in threads_live + threads_offline:
t.join(timeout=1)
thread_result_consumer.join(timeout=1) thread_result_consumer.join(timeout=1)
@@ -554,7 +778,7 @@ def format_time(seconds: float | int) -> str:
return "0:00" return "0:00"
@app.get("/team1.json") @app.get("/team1")
async def team1(): async def team1():
game = get_latest_game_safe() game = get_latest_game_safe()
if not game: if not game:
@@ -562,7 +786,7 @@ async def team1():
return await team("team1") return await team("team1")
@app.get("/team2.json") @app.get("/team2")
async def team2(): async def team2():
game = get_latest_game_safe() game = get_latest_game_safe()
if not game: if not game:
@@ -570,36 +794,36 @@ async def team2():
return await team("team2") return await team("team2")
@app.get("/top_team1.json") @app.get("/top_team1")
async def top_team1(): async def top_team1():
data = await team("team1") data = await team("team1")
return await top_sorted_team(data) return await top_sorted_team(data)
@app.get("/top_team2.json") @app.get("/top_team2")
async def top_team2(): async def top_team2():
data = await team("team2") data = await team("team2")
return await top_sorted_team(data) return await top_sorted_team(data)
@app.get("/started_team1.json") @app.get("/started_team1")
async def started_team1(): async def started_team1():
data = await team("team1") data = await team("team1")
return await started_team(data) return await started_team(data)
@app.get("/started_team2.json") @app.get("/started_team2")
async def started_team2(): async def started_team2():
data = await team("team2") data = await team("team2")
return await started_team(data) return await started_team(data)
@app.get("/game.json") @app.get("/game")
async def game(): async def game():
return latest_data["game"] return latest_data["game"]
@app.get("/status.json") @app.get("/status")
async def status(request: Request): async def status(request: Request):
def color_for_status(status_value: str) -> str: def color_for_status(status_value: str) -> str:
"""Подбор цвета статуса в HEX""" """Подбор цвета статуса в HEX"""
@@ -608,13 +832,25 @@ async def status(request: Request):
return "#00FF00" # зелёный return "#00FF00" # зелёный
elif status_value in ["scheduled", "today_not_started", "upcoming"]: elif status_value in ["scheduled", "today_not_started", "upcoming"]:
return "#FFFF00" # жёлтый return "#FFFF00" # жёлтый
elif status_value in ["result", "resultconfirmed", "finished", "finished_today"]: elif status_value in [
"result",
"resultconfirmed",
"finished",
"finished_today",
]:
return "#FF0000" # красный return "#FF0000" # красный
elif status_value in ["no_game_today", "unknown", "none"]: elif status_value in ["no_game_today", "unknown", "none"]:
return "#FFFFFF" # белый return "#FFFFFF" # белый
else: else:
return "#808080" # серый (неизвестный статус) return "#808080" # серый (неизвестный статус)
# ✳️ сортируем latest_data в нужном порядке
sort_order = ["game", "live-status", "box-score", "play-by-play"]
sorted_keys = (
[k for k in sort_order if k in latest_data] +
sorted([k for k in latest_data if k not in sort_order])
)
data = { data = {
"league": LEAGUE, "league": LEAGUE,
"team": TEAM, "team": TEAM,
@@ -624,16 +860,20 @@ async def status(request: Request):
{ {
"name": TEAM, "name": TEAM,
"status": STATUS, "status": STATUS,
"ts": GAME_START_DT.strftime("%Y-%m-%d %H:%M") if GAME_START_DT else "N/A", "ts": (
GAME_START_DT.strftime("%Y-%m-%d %H:%M") if GAME_START_DT else "N/A"
),
"link": LEAGUE, "link": LEAGUE,
"color": color_for_status(STATUS) # ← добавлено "color": color_for_status(STATUS),
} }
] + [ ]
+ [
{ {
"name": item, "name": item,
"status": ( "status": (
latest_data[item]["data"]["status"] latest_data[item]["data"]["status"]
if isinstance(latest_data[item]["data"], dict) and "status" in latest_data[item]["data"] if isinstance(latest_data[item]["data"], dict)
and "status" in latest_data[item]["data"]
else latest_data[item]["data"] else latest_data[item]["data"]
), ),
"ts": latest_data[item]["ts"], "ts": latest_data[item]["ts"],
@@ -646,14 +886,15 @@ async def status(request: Request):
), ),
"color": color_for_status( "color": color_for_status(
latest_data[item]["data"]["status"] latest_data[item]["data"]["status"]
if isinstance(latest_data[item]["data"], dict) and "status" in latest_data[item]["data"] if isinstance(latest_data[item]["data"], dict)
and "status" in latest_data[item]["data"]
else latest_data[item]["data"] else latest_data[item]["data"]
) # ← добавлено ),
} }
for item in latest_data for item in sorted_keys # ← используем отсортированный порядок
], ],
} }
accept = request.headers.get("accept", "") accept = request.headers.get("accept", "")
if "text/html" in accept: if "text/html" in accept:
status_raw = str(STATUS).lower() status_raw = str(STATUS).lower()
@@ -675,7 +916,7 @@ async def status(request: Request):
else: else:
gs_class = "unknown" gs_class = "unknown"
gs_text = "⚪ UNKNOWN" gs_text = "⚪ UNKNOWN"
html = f""" html = f"""
<html> <html>
<head> <head>
@@ -732,11 +973,19 @@ async def status(request: Request):
for s in data["statuses"]: for s in data["statuses"]:
status_text = str(s["status"]).strip().lower() status_text = str(s["status"]).strip().lower()
if any(x in status_text for x in ["ok", "success", "live", "live_soon", "online"]): if any(
x in status_text
for x in ["ok", "success", "live", "live_soon", "online"]
):
color_class = "ok" color_class = "ok"
elif any(x in status_text for x in ["scheduled", "today_not_started", "upcoming"]): elif any(
x in status_text for x in ["scheduled", "today_not_started", "upcoming"]
):
color_class = "warn" color_class = "warn"
elif any(x in status_text for x in ["result", "resultconfirmed", "finished", "finished_today"]): elif any(
x in status_text
for x in ["result", "resultconfirmed", "finished", "finished_today"]
):
color_class = "fail" color_class = "fail"
else: else:
color_class = "unknown" color_class = "unknown"
@@ -762,7 +1011,8 @@ async def status(request: Request):
response.headers["Refresh"] = "1" response.headers["Refresh"] = "1"
return response return response
@app.get("/scores.json")
@app.get("/scores")
async def scores(): async def scores():
game = get_latest_game_safe() game = get_latest_game_safe()
if not game: if not game:
@@ -803,7 +1053,6 @@ async def scores():
return score_by_quarter return score_by_quarter
async def top_sorted_team(data): async def top_sorted_team(data):
top_sorted_team = sorted( top_sorted_team = sorted(
(p for p in data if p.get("startRole") in ["Player", ""]), (p for p in data if p.get("startRole") in ["Player", ""]),
@@ -869,7 +1118,9 @@ async def team(who: str):
# нормализуем доступ к данным # нормализуем доступ к данным
game_data = game["data"] if "data" in game else game game_data = game["data"] if "data" in game else game
result = game_data["result"] # здесь уже безопасно, мы проверили в get_latest_game_safe result = game_data[
"result"
] # здесь уже безопасно, мы проверили в get_latest_game_safe
# в result ожидаем "teams" # в result ожидаем "teams"
teams = result.get("teams") teams = result.get("teams")
@@ -1069,6 +1320,7 @@ async def team(who: str):
return sorted_team return sorted_team
async def started_team(data): async def started_team(data):
started_team = sorted( started_team = sorted(
( (
@@ -1285,7 +1537,7 @@ stat_name_list = [
] ]
@app.get("/team_stats.json") @app.get("/team_stats")
async def team_stats(): async def team_stats():
teams = latest_data["game"]["data"]["result"]["teams"] teams = latest_data["game"]["data"]["result"]["teams"]
plays = latest_data["game"]["data"]["result"]["plays"] plays = latest_data["game"]["data"]["result"]["plays"]
@@ -1339,7 +1591,7 @@ async def team_stats():
return result_json return result_json
@app.get("/referee.json") @app.get("/referee")
async def referee(): async def referee():
desired_order = [ desired_order = [
"Crew chief", "Crew chief",
@@ -1391,7 +1643,7 @@ async def referee():
return referees return referees
@app.get("/team_comparison.json") @app.get("/team_comparison")
async def team_comparison(): async def team_comparison():
if STATUS not in ["no_game_today", "finished_today"]: if STATUS not in ["no_game_today", "finished_today"]:
data = latest_data["pregame"]["data"]["result"] data = latest_data["pregame"]["data"]["result"]
@@ -1467,9 +1719,7 @@ async def team_comparison():
return [{"Данных о сравнении команд нет!"}] return [{"Данных о сравнении команд нет!"}]
@app.get("/standings")
@app.get("/standings.json")
async def regular_standings(): async def regular_standings():
data = latest_data["actual-standings"]["data"]["items"] data = latest_data["actual-standings"]["data"]["items"]
for item in data: for item in data:
@@ -1540,7 +1790,7 @@ async def regular_standings():
return standings_payload return standings_payload
@app.get("/live_status.json") @app.get("/live_status")
async def live_status(): async def live_status():
# если матч реально идёт/вот-вот — пытаемся отдать то, что есть # если матч реально идёт/вот-вот — пытаемся отдать то, что есть
if STATUS in ["live", "live_soon"]: if STATUS in ["live", "live_soon"]:
@@ -1563,9 +1813,7 @@ async def live_status():
# 2) если это просто строка статуса ("ok" / "no-status" / "error") # 2) если это просто строка статуса ("ok" / "no-status" / "error")
if isinstance(raw, str): if isinstance(raw, str):
return [{ return [{"status": raw}]
"status": raw
}]
# fallback # fallback
return [{"foulsA": 0, "foulsB": 0}] return [{"foulsA": 0, "foulsB": 0}]
@@ -1575,4 +1823,4 @@ async def live_status():
if __name__ == "__main__": if __name__ == "__main__":
uvicorn.run("get_data:app", host="0.0.0.0", port=8000, reload=True) uvicorn.run("get_data:app", host="0.0.0.0", port=8000, reload=True, log_level="critical")