Compare commits
7 Commits
101a5a09d3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| eff04e68f4 | |||
| aec3fccfea | |||
| bfa74b51c3 | |||
| a317dd9d6a | |||
| 9efb351522 | |||
|
|
04574f5621 | ||
|
|
9aa698dcfe |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -4,3 +4,4 @@
|
|||||||
*.venv
|
*.venv
|
||||||
*.env
|
*.env
|
||||||
/shotmaps/*
|
/shotmaps/*
|
||||||
|
get_data copy.py
|
||||||
@@ -507,9 +507,6 @@ main() {
|
|||||||
# Загрузка кода
|
# Загрузка кода
|
||||||
download_code "$release"
|
download_code "$release"
|
||||||
|
|
||||||
# Проверка файлов
|
|
||||||
check_required_files
|
|
||||||
|
|
||||||
# Настройка виртуального окружения
|
# Настройка виртуального окружения
|
||||||
setup_venv
|
setup_venv
|
||||||
|
|
||||||
|
|||||||
136
get_data.py
136
get_data.py
@@ -1,5 +1,5 @@
|
|||||||
from fastapi import FastAPI, HTTPException, Request
|
from fastapi import FastAPI, HTTPException, Request
|
||||||
from fastapi.responses import Response, HTMLResponse, StreamingResponse
|
from fastapi.responses import Response, HTMLResponse, StreamingResponse, JSONResponse
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
import requests, uvicorn, json
|
import requests, uvicorn, json
|
||||||
@@ -14,7 +14,7 @@ from dotenv import load_dotenv
|
|||||||
from pprint import pprint
|
from pprint import pprint
|
||||||
import nasio
|
import nasio
|
||||||
import io, os, platform, time
|
import io, os, platform, time
|
||||||
import xml.etree.ElementTree as ETя
|
import xml.etree.ElementTree as ET
|
||||||
import re
|
import re
|
||||||
from PIL import Image, ImageDraw, ImageFont
|
from PIL import Image, ImageDraw, ImageFont
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
@@ -202,6 +202,8 @@ URLS = {
|
|||||||
"live-status": "{host}api/abc/games/live-status?id={game_id}",
|
"live-status": "{host}api/abc/games/live-status?id={game_id}",
|
||||||
"box-score": "{host}api/abc/games/box-score?id={game_id}",
|
"box-score": "{host}api/abc/games/box-score?id={game_id}",
|
||||||
"play-by-play": "{host}api/abc/games/play-by-play?id={game_id}",
|
"play-by-play": "{host}api/abc/games/play-by-play?id={game_id}",
|
||||||
|
"players-stats-league": "{host}/api/abc/comps/players-stats?tag={league}&lang={lang}&maxResultCount=10000",
|
||||||
|
"players-stats-season": "{host}/api/abc/comps/players-stats?tag={league}&season={season}&lang={lang}&maxResultCount=10000",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1398,6 +1400,37 @@ async def lifespan(app: FastAPI):
|
|||||||
)
|
)
|
||||||
thread_excel.start()
|
thread_excel.start()
|
||||||
|
|
||||||
|
try:
|
||||||
|
season_stats = requests.get(
|
||||||
|
URLS["players-stats-season"].format(
|
||||||
|
host=HOST, league=LEAGUE, season=season, lang=LANG
|
||||||
|
)
|
||||||
|
).json()
|
||||||
|
except Exception as ex:
|
||||||
|
season_stats = None
|
||||||
|
try:
|
||||||
|
league_stats = requests.get(
|
||||||
|
URLS["players-stats-league"].format(host=HOST, league=LEAGUE, lang=LANG)
|
||||||
|
).json()
|
||||||
|
except Exception as ex:
|
||||||
|
league_stats = None
|
||||||
|
|
||||||
|
# 👇 ДОБАВЬ ЭТО
|
||||||
|
ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
||||||
|
|
||||||
|
if season_stats is not None:
|
||||||
|
latest_data["season_stats"] = {
|
||||||
|
"ts": ts,
|
||||||
|
"data": season_stats,
|
||||||
|
}
|
||||||
|
|
||||||
|
if league_stats is not None:
|
||||||
|
latest_data["league_stats"] = {
|
||||||
|
"ts": ts,
|
||||||
|
"data": league_stats,
|
||||||
|
}
|
||||||
|
# 👆 ДО СЮДА
|
||||||
|
|
||||||
# 4. запускаем "длинные" потоки (они у тебя и так всегда)
|
# 4. запускаем "длинные" потоки (они у тебя и так всегда)
|
||||||
thread_result_consumer = threading.Thread(
|
thread_result_consumer = threading.Thread(
|
||||||
target=results_consumer,
|
target=results_consumer,
|
||||||
@@ -1473,16 +1506,6 @@ app = FastAPI(
|
|||||||
redoc_url=None, # ❌ отключает /redoc
|
redoc_url=None, # ❌ отключает /redoc
|
||||||
openapi_url=None, # ❌ отключает /openapi.json
|
openapi_url=None, # ❌ отключает /openapi.json
|
||||||
)
|
)
|
||||||
# раздаём /shotmaps как статику из SHOTMAP_DIR
|
|
||||||
# app.mount("/shotmaps", StaticFiles(directory=SHOTMAP_DIR), name="shotmaps")
|
|
||||||
|
|
||||||
# @app.get("/shotmaps/{filename}")
|
|
||||||
# async def get_shotmap(filename: str):
|
|
||||||
# data = SHOTMAP_CACHE.get(filename)
|
|
||||||
# if not data:
|
|
||||||
# # если вдруг перезапустился процесс или такой карты нет
|
|
||||||
# raise HTTPException(status_code=404, detail="Shotmap not found")
|
|
||||||
# return Response(content=data, media_type="image/png")
|
|
||||||
|
|
||||||
|
|
||||||
def format_time(seconds: float | int) -> str:
|
def format_time(seconds: float | int) -> str:
|
||||||
@@ -1709,6 +1732,7 @@ async def status(request: Request):
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
for item in sorted_keys
|
for item in sorted_keys
|
||||||
|
if item not in ["league_stats", "season_stats"]
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2246,7 +2270,12 @@ async def team(who: str):
|
|||||||
f"{item.get('displayNumber')}.png",
|
f"{item.get('displayNumber')}.png",
|
||||||
)
|
)
|
||||||
if item.get("startRole") == "Player"
|
if item.get("startRole") == "Player"
|
||||||
else ""
|
else os.path.join(
|
||||||
|
"D:\\Photos",
|
||||||
|
result["league"]["abcName"],
|
||||||
|
result[who]["name"],
|
||||||
|
f'Head Coach_{(item.get("lastName") or "").strip()} {(item.get("firstName") or "").strip()}.png',
|
||||||
|
)
|
||||||
),
|
),
|
||||||
# live-стата
|
# live-стата
|
||||||
"pts": _as_int(stats.get("points")),
|
"pts": _as_int(stats.get("points")),
|
||||||
@@ -3237,9 +3266,11 @@ def change_vmix_datasource_urls(xml_data, new_base_url: str) -> bytes:
|
|||||||
root = ET.fromstring(text)
|
root = ET.fromstring(text)
|
||||||
|
|
||||||
# 4. Меняем URL
|
# 4. Меняем URL
|
||||||
for url_tag in root.findall(".//datasource[@friendlyName='JSON']//instance//state/xml/url"):
|
for url_tag in root.findall(
|
||||||
|
".//datasource[@friendlyName='JSON']//instance//state/xml/url"
|
||||||
|
):
|
||||||
old_url = url_tag.text.strip()
|
old_url = url_tag.text.strip()
|
||||||
pattern = r'https?:\/\/[^\/]+'
|
pattern = r"https?:\/\/[^\/]+"
|
||||||
new_url = re.sub(pattern, new_base_url, old_url, count=0, flags=0)
|
new_url = re.sub(pattern, new_base_url, old_url, count=0, flags=0)
|
||||||
url_tag.text = new_url
|
url_tag.text = new_url
|
||||||
|
|
||||||
@@ -3633,6 +3664,7 @@ async def get_shotmap_image(player_id_shots: str):
|
|||||||
Отдаёт картинку карты бросков из оперативной памяти.
|
Отдаёт картинку карты бросков из оперативной памяти.
|
||||||
player_id_shots должен совпадать с ключом в SHOTMAP_CACHE, например "23_shots".
|
player_id_shots должен совпадать с ключом в SHOTMAP_CACHE, например "23_shots".
|
||||||
"""
|
"""
|
||||||
|
# async with SHOTMAPS_LOCK:
|
||||||
data = SHOTMAP_CACHE.get(player_id_shots)
|
data = SHOTMAP_CACHE.get(player_id_shots)
|
||||||
if not data:
|
if not data:
|
||||||
raise HTTPException(status_code=404, detail="Shotmap not found in memory")
|
raise HTTPException(status_code=404, detail="Shotmap not found in memory")
|
||||||
@@ -3738,6 +3770,30 @@ async def last_5_games():
|
|||||||
if not CALENDAR or "items" not in CALENDAR:
|
if not CALENDAR or "items" not in CALENDAR:
|
||||||
return {"opponent": "", "date": "", "place": "", "place_ru": ""}
|
return {"opponent": "", "date": "", "place": "", "place_ru": ""}
|
||||||
|
|
||||||
|
WEEKDAYS_EN = [
|
||||||
|
"monday",
|
||||||
|
"tuesday",
|
||||||
|
"wednesday",
|
||||||
|
"thursday",
|
||||||
|
"friday",
|
||||||
|
"saturday",
|
||||||
|
"sunday",
|
||||||
|
]
|
||||||
|
MONTHS_EN = [
|
||||||
|
"january",
|
||||||
|
"february",
|
||||||
|
"march",
|
||||||
|
"april",
|
||||||
|
"may",
|
||||||
|
"june",
|
||||||
|
"july",
|
||||||
|
"august",
|
||||||
|
"september",
|
||||||
|
"october",
|
||||||
|
"november",
|
||||||
|
"december",
|
||||||
|
]
|
||||||
|
|
||||||
now = datetime.now() # наивное "сейчас"
|
now = datetime.now() # наивное "сейчас"
|
||||||
best = None # {"dt": ..., "opp": ..., "place": "home"/"away"}
|
best = None # {"dt": ..., "opp": ..., "place": "home"/"away"}
|
||||||
|
|
||||||
@@ -3779,11 +3835,23 @@ async def last_5_games():
|
|||||||
|
|
||||||
place_ru = "дома" if best["place"] == "home" else "в гостях"
|
place_ru = "дома" if best["place"] == "home" else "в гостях"
|
||||||
|
|
||||||
|
# 🆕 формируем английскую строку
|
||||||
|
dt = best["dt"]
|
||||||
|
weekday_en = WEEKDAYS_EN[dt.weekday()] # monday..sunday
|
||||||
|
month_en = MONTHS_EN[dt.month - 1] # january..december
|
||||||
|
day = dt.day # 1..31
|
||||||
|
place_en = "home" if best["place"] == "home" else "away"
|
||||||
|
|
||||||
|
formatted = (
|
||||||
|
f"{weekday_en}, {month_en} {day}, at {place_en} against {best['opp']}"
|
||||||
|
)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"opponent": best["opp"],
|
"opponent": best["opp"],
|
||||||
"date": best["dt"].strftime("%Y-%m-%d %H:%M"),
|
"date": best["dt"].strftime("%Y-%m-%d %H:%M"),
|
||||||
"place": best["place"], # "home" / "away"
|
"place": best["place"], # "home" / "away"
|
||||||
"place_ru": place_ru, # "дома" / "в гостях"
|
"place_ru": place_ru, # "дома" / "в гостях"
|
||||||
|
"formatted": formatted, # 🆕 "wednesday, march 26, at home against astana"
|
||||||
}
|
}
|
||||||
|
|
||||||
# последние 5 игр и результаты
|
# последние 5 игр и результаты
|
||||||
@@ -3806,6 +3874,7 @@ async def last_5_games():
|
|||||||
"nextGameDate": next1["date"],
|
"nextGameDate": next1["date"],
|
||||||
"nextGamePlace": next1["place_ru"], # "дома" / "в гостях"
|
"nextGamePlace": next1["place_ru"], # "дома" / "в гостях"
|
||||||
"nextGameHomeAway": next1["place"], # "home" / "away" (если нужно в логике)
|
"nextGameHomeAway": next1["place"], # "home" / "away" (если нужно в логике)
|
||||||
|
"nextGameFormatted": next1["formatted"],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"teamName": team2_name,
|
"teamName": team2_name,
|
||||||
@@ -3815,6 +3884,7 @@ async def last_5_games():
|
|||||||
"nextGameDate": next2["date"],
|
"nextGameDate": next2["date"],
|
||||||
"nextGamePlace": next2["place_ru"],
|
"nextGamePlace": next2["place_ru"],
|
||||||
"nextGameHomeAway": next2["place"],
|
"nextGameHomeAway": next2["place"],
|
||||||
|
"nextGameFormatted": next2["formatted"],
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
return data
|
return data
|
||||||
@@ -3916,7 +3986,8 @@ async def commentary():
|
|||||||
plus_minus = p.get("plusMinus", "")
|
plus_minus = p.get("plusMinus", "")
|
||||||
kpi = p.get("kpi", "")
|
kpi = p.get("kpi", "")
|
||||||
|
|
||||||
rows.append(f"""
|
rows.append(
|
||||||
|
f"""
|
||||||
<tr>
|
<tr>
|
||||||
<td>{num}</td>
|
<td>{num}</td>
|
||||||
<td class="player-name"
|
<td class="player-name"
|
||||||
@@ -3939,7 +4010,8 @@ async def commentary():
|
|||||||
<td class="extra-col">{plus_minus}</td>
|
<td class="extra-col">{plus_minus}</td>
|
||||||
<td class="extra-col">{kpi}</td>
|
<td class="extra-col">{kpi}</td>
|
||||||
</tr>
|
</tr>
|
||||||
""")
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
return f"""
|
return f"""
|
||||||
<h3>{title}</h3>
|
<h3>{title}</h3>
|
||||||
@@ -4458,7 +4530,7 @@ async def dashboard():
|
|||||||
|
|
||||||
# порядок вывода метрик в центральном столбце (как на твоём дашборде)
|
# порядок вывода метрик в центральном столбце (как на твоём дашборде)
|
||||||
center_stats_order = [
|
center_stats_order = [
|
||||||
"pt-1", # штрафные
|
"pt-1", # штрафные
|
||||||
"pt-1_pro",
|
"pt-1_pro",
|
||||||
"pt-2",
|
"pt-2",
|
||||||
"pt-2_pro",
|
"pt-2_pro",
|
||||||
@@ -4901,6 +4973,34 @@ async def dashboard():
|
|||||||
return HTMLResponse(content=html)
|
return HTMLResponse(content=html)
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/game_history")
|
||||||
|
async def game_history():
|
||||||
|
pregame = get_latest_game_safe("pregame")
|
||||||
|
if not pregame:
|
||||||
|
return [{"Данных об истории команд нет!"}]
|
||||||
|
|
||||||
|
pregame_data = pregame["data"] if "data" in pregame else pregame
|
||||||
|
result = pregame_data.get("result", {}).get("gameHistory", {}) or {}
|
||||||
|
history = []
|
||||||
|
|
||||||
|
for row in result:
|
||||||
|
row_team1 = get_excel_row_for_team(row["team1"]["name"]) or {}
|
||||||
|
row_team2 = get_excel_row_for_team(row["team2"]["name"]) or {}
|
||||||
|
history.append(
|
||||||
|
{
|
||||||
|
"team1": row_team1.get("TeamTLA", ""),
|
||||||
|
"team2": row_team2.get("TeamTLA", ""),
|
||||||
|
"team1_logo": row_team1.get("TeamLogo", ""),
|
||||||
|
"team2_logo": row_team2.get("TeamLogo", ""),
|
||||||
|
"score1": row["game"]["score1"],
|
||||||
|
"score2": row["game"]["score2"],
|
||||||
|
"localDate": row["game"]["localDate"],
|
||||||
|
"team1Win": pregame_data.get("result", {}).get("team1Win"),
|
||||||
|
"team2Win": pregame_data.get("result", {}).get("team2Win"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return history
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
uvicorn.run(
|
uvicorn.run(
|
||||||
"get_data:app", host="0.0.0.0", port=8000, reload=True, log_level="debug"
|
"get_data:app", host="0.0.0.0", port=8000, reload=True, log_level="debug"
|
||||||
|
|||||||
Reference in New Issue
Block a user