From 0d06097181b008f1cde84313f255d8e05bbe703a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=AE=D1=80=D0=B8=D0=B9=20=D0=A7=D0=B5=D1=80=D0=BD=D0=B5?= =?UTF-8?q?=D0=BD=D0=BA=D0=BE?= Date: Tue, 11 Nov 2025 19:46:41 +0300 Subject: [PATCH 1/5] =?UTF-8?q?=D0=B2=D0=B5=D1=80=D1=81=D0=B8=D1=8F=20?= =?UTF-8?q?=D1=81=20=D1=87=D1=82=D0=B5=D0=BD=D0=B8=D0=B5=D0=BC=20=D0=B8=20?= =?UTF-8?q?=D0=BF=D0=B5=D1=80=D0=B5=D0=B4=D0=B0=D1=87=D0=B5=D0=B9=20=D0=B1?= =?UTF-8?q?=D0=B8=D0=BD=D0=B0=D1=80=D0=BD=D0=BE=D0=B3=D0=BE=20=D1=84=D0=B0?= =?UTF-8?q?=D0=B9=D0=BB=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + get_data.py | 330 +++++++++++++++++++++++++++++++++------------------- 2 files changed, 211 insertions(+), 120 deletions(-) diff --git a/.gitignore b/.gitignore index 236c12b..61009a9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ .env /.venv +logs/ /logs/* __pycache__/ *.pyc diff --git a/get_data.py b/get_data.py index cba2592..780b23f 100644 --- a/get_data.py +++ b/get_data.py @@ -1,5 +1,5 @@ -from fastapi import FastAPI, HTTPException, Query -from fastapi.responses import Response, JSONResponse, HTMLResponse +from fastapi import FastAPI, HTTPException, Query, Request +from fastapi.responses import Response, JSONResponse, HTMLResponse, StreamingResponse from fastapi.encoders import jsonable_encoder import pandas as pd import requests, io, os @@ -12,26 +12,31 @@ import time from contextlib import asynccontextmanager import numpy as np import nasio +import logging +import logging.config +import platform +import json +from pprint import pprint # --- Глобальные переменные --- selected_game_id: int | None = None -current_tournament_id: int | None = None # будем обновлять при загрузке расписания -current_season: str | None = None # будем обновлять при загрузке расписания -latest_game_data: dict | None = None # сюда кладём последние данные по матчу +current_tournament_id: int | None = None # будем обновлять при загрузке расписания +current_season: str | None = None # будем обновлять при загрузке расписания +latest_game_data: dict | None = None # сюда кладём последние данные по матчу latest_game_error: str | None = None _latest_lock = Lock() _stop_event = Event() _worker_thread: Thread | None = None # Загружаем переменные из .env -if load_dotenv(dotenv_path="/mnt/khl/.env",verbose=True): +if load_dotenv(dotenv_path="/mnt/khl/.env", verbose=True): print("Добавить в лог что был найден файл окружения!!") pass else: load_dotenv() print("Добавить в лог что не был найден файл окружения!!") - + api_user = os.getenv("API_USER") api_pass = os.getenv("API_PASS") league = os.getenv("LEAGUE") @@ -42,7 +47,6 @@ PASSWORD = os.getenv("SYNO_PASSWORD") PATH = "/team-folders/GFX/Hockey/KHL/Soft/MATCH.xlsm" - def load_today_schedule(): """Возвращает DataFrame матчей на сегодня с нужными колонками (или пустой DF).""" url_tournaments = "http://stat2tv.khl.ru/tournaments.xml" @@ -74,7 +78,17 @@ def load_today_schedule(): schedule_df = pd.read_xml(io.StringIO(r.text)) # Нужные колонки (скорректируй под реальные имена из XML) - needed_columns = ["id", "date", "time", "homeName_en", "visitorName_en", "arena_en", "arena_city_en", "homeCity_en", "visitorCity_en"] + needed_columns = [ + "id", + "date", + "time", + "homeName_en", + "visitorName_en", + "arena_en", + "arena_city_en", + "homeCity_en", + "visitorCity_en", + ] exist = [c for c in needed_columns if c in schedule_df.columns] schedule_df = schedule_df[exist].copy() @@ -124,10 +138,7 @@ def _fetch_game_once(tournament_id: int, game_id: int) -> dict: """Один запрос к API матча -> чистый JSON из API.""" url = _build_game_url(tournament_id, game_id) r = requests.get( - url, - auth=HTTPBasicAuth(api_user, api_pass), - verify=False, - timeout=10 + url, auth=HTTPBasicAuth(api_user, api_pass), verify=False, timeout=10 ) r.raise_for_status() @@ -183,17 +194,18 @@ async def lifespan(app: FastAPI): _worker_thread.join(timeout=2) print("🛑 Background thread stopped") + app = FastAPI( lifespan=lifespan, - docs_url=None, # ❌ отключает /docs - redoc_url=None, # ❌ отключает /redoc - openapi_url=None # ❌ отключает /openapi.json + docs_url=None, # ❌ отключает /docs + redoc_url=None, # ❌ отключает /redoc + openapi_url=None, # ❌ отключает /openapi.json ) @app.get("/games") async def games(): - + df = load_today_schedule() if df.empty: return JSONResponse({"message": "Сегодня матчей нет"}) @@ -202,8 +214,6 @@ async def games(): return Response(content=json_schedule, media_type="application/json") - - @app.get("/select") async def select(): df = load_today_schedule() @@ -336,13 +346,15 @@ async def get_selected_game(): async def game_url(): if not (selected_game_id and current_tournament_id): return JSONResponse({"message": "game_id или tournament_id не задан"}) - return JSONResponse({ - "url": _build_game_url(current_tournament_id, selected_game_id), - "game_id": selected_game_id, - "tournament_id": current_tournament_id - }) - - + return JSONResponse( + { + "url": _build_game_url(current_tournament_id, selected_game_id), + "game_id": selected_game_id, + "tournament_id": current_tournament_id, + } + ) + + # @app.get("/info") # async def info(): # if selected_game_id: @@ -356,7 +368,9 @@ async def game_data(): return JSONResponse(latest_game_data) if latest_game_error: return JSONResponse({"error": latest_game_error}, status_code=502) - return JSONResponse({"message": "Ещё нет данных. Выберите матч и подождите первое обновление."}) + return JSONResponse( + {"message": "Ещё нет данных. Выберите матч и подождите первое обновление."} + ) @app.get("/referee") @@ -383,15 +397,14 @@ async def referee(): return data_referees - -async def team(who:str): - """"who: A - домашняя команда, B - гостевая""" +async def team(who: str): + """ "who: A - домашняя команда, B - гостевая""" with _latest_lock: lgd = latest_game_data # print(lgd) if not lgd or "data" not in lgd: - return [{"details":"Нет данных по матчу!"}] - + return [{"details": "Нет данных по матчу!"}] + players1_temp = lgd["data"]["players"][who] players1 = [] players1_f = [] @@ -426,7 +439,7 @@ async def team(who:str): position = "forward" elif players1_temp[player]["ps"] == "d": position = "defenseman" - + data_with_number = { "number": player, "NameSurnameGFX": names_new + " " + lastname1, @@ -462,7 +475,7 @@ async def team(who:str): } players1_g.append(data_with_number2) goaltenders1.append(data_with_number2) - + def make_empty(example_list): if not example_list: return {} @@ -479,20 +492,22 @@ async def team(who:str): players1_f.append(empty_f.copy()) while len(players1_g) < 3: players1_g.append(empty_g.copy()) - + players1 = players1_d + players1_f + players1_g # print(len(players1)) return players1 - + @app.get("/team1") async def team1(): return await team("A") - + + @app.get("/team2") async def team2(): return await team("B") + # 👉 метка для первой строки (period row) def _period_label(period: str | int) -> str: s = str(period).strip().upper() @@ -516,6 +531,7 @@ def _period_label(period: str | int) -> str: return "FINAL" return "" + # 👉 сортировка периодов: 1,2,3, затем OT/OT2/... def _period_sort_key(k: str) -> tuple[int, int]: s = str(k).strip().upper() @@ -578,19 +594,25 @@ def format_team_stat(team1: dict, team2: dict, period: str | None = None) -> lis teams = [{k: str(v) for k, v in t.items()} for t in [team1, team2]] keys = list(teams[0].keys()) - + formatted = [] - + if period is not None: - formatted.append({ - "name0": "period", - "name1": str(period), - "name2": "", - "StatParameterGFX": _period_label(period) or "Period" - }) - + formatted.append( + { + "name0": "period", + "name1": str(period), + "name2": "", + "StatParameterGFX": _period_label(period) or "Period", + } + ) + for key in keys: - row = {"name0": key, "name1": teams[0].get(key, ""), "name2": teams[1].get(key, "")} + row = { + "name0": key, + "name1": teams[0].get(key, ""), + "name2": teams[1].get(key, ""), + } # подписи for code, eng, _ru in stat_list: if key == code: @@ -608,6 +630,7 @@ def format_team_stat(team1: dict, team2: dict, period: str | None = None) -> lis def flip(s: str) -> str: parts = s.split() return f"{parts[1]} {parts[0]}" if len(parts) >= 2 else s + r["name1"] = flip(r["name1"]) r["name2"] = flip(r["name2"]) @@ -622,11 +645,13 @@ def _iter_period_pairs(teams_periods: dict): """ a = teams_periods.get("A", {}) b = teams_periods.get("B", {}) + def _key(k): try: return int(k) except (TypeError, ValueError): return k + for k in sorted(a.keys(), key=_key): if k in b: yield k, a[k], b[k] @@ -638,22 +663,20 @@ def _build_all_stats(payload: dict) -> dict: """ total_a = payload["teams"]["A"] total_b = payload["teams"]["B"] - result = { - "total": format_team_stat(total_a, total_b), - "periods": [] - } + result = {"total": format_team_stat(total_a, total_b), "periods": []} for period_key, a_stat, b_stat in _iter_period_pairs(payload["teams_periods"]): - result["periods"].append({ - "period": period_key, - "stats": format_team_stat(a_stat, b_stat) - }) + result["periods"].append( + {"period": period_key, "stats": format_team_stat(a_stat, b_stat)} + ) return result @app.get("/teams/stats") async def teams_stats( scope: str = Query("all", pattern="^(all|total|period)$"), - n: str | None = Query(None, description="Номер периода (строка или число) при scope=period"), + n: str | None = Query( + None, description="Номер периода (строка или число) при scope=period" + ), ): """ Все-в-одном: GET /teams/stats?scope=all @@ -671,7 +694,9 @@ async def teams_stats( payload = lgd["data"] if scope == "total": - data = format_team_stat(payload["teams"]["A"], payload["teams"]["B"], period="Total") + data = format_team_stat( + payload["teams"]["A"], payload["teams"]["B"], period="Total" + ) return JSONResponse({"scope": "total", "data": data}) if scope == "period": @@ -688,21 +713,23 @@ async def teams_stats( a = payload["teams_periods"]["A"].get(period_key) b = payload["teams_periods"]["B"].get(period_key) if a is None or b is None: - raise HTTPException(status_code=404, detail=f"Период {period_key} не найден.") - return JSONResponse({ - "scope": "period", - "period": period_key, - "is_current": period_key == _current_period_key(payload), - "data": format_team_stat(a, b, period=period_key) - }) + raise HTTPException( + status_code=404, detail=f"Период {period_key} не найден." + ) + return JSONResponse( + { + "scope": "period", + "period": period_key, + "is_current": period_key == _current_period_key(payload), + "data": format_team_stat(a, b, period=period_key), + } + ) # scope == "all" cur = _current_period_key(payload) - return JSONResponse({ - "scope": "all", - "current_period": cur, - "data": _build_all_stats(payload) - }) + return JSONResponse( + {"scope": "all", "current_period": cur, "data": _build_all_stats(payload)} + ) def _norm_name(s: str | None) -> str: @@ -711,8 +738,16 @@ def _norm_name(s: str | None) -> str: return "" return str(s).strip().casefold() +def _load_buf(): + buf = nasio.load_bio(user=USER, password=PASSWORD, + nas_ip=SERVER_NAME, nas_port="443", path=PATH) + if isinstance(buf, (bytes, bytearray, memoryview)): + buf = io.BytesIO(buf) + buf.seek(0) + return buf + @app.get("/info") -async def info(): +async def info(format: str = "xlsx", sheet: str = "TEAMS"): # 1) Проверяем, выбран ли матч global current_season if not selected_game_id: @@ -721,73 +756,128 @@ async def info(): # 2) Берём расписание и ищем строку по выбранному ID df = load_today_schedule() if df.empty: - return JSONResponse({"message": "Сегодня матчей нет", "selected_id": selected_game_id}) + return JSONResponse( + {"message": "Сегодня матчей нет", "selected_id": selected_game_id} + ) # безопасно приводим id к int и ищем try: row = df.loc[df["id"].astype(int) == int(selected_game_id)].iloc[0] except Exception: - return JSONResponse({"message": "Выбранный матч не найден в расписании на сегодня", - "selected_id": selected_game_id}, status_code=404) + return JSONResponse( + { + "message": "Выбранный матч не найден в расписании на сегодня", + "selected_id": selected_game_id, + }, + status_code=404, + ) home_name = str(row.get("homeName_en", "")).strip() away_name = str(row.get("visitorName_en", "")).strip() # 3) Подтягиваем справочник команд из Excel (лист TEAMS) - teams_df = nasio.load_formatted( - user=USER, - password=PASSWORD, - nas_ip=SERVER_NAME, - nas_port="443", - path=PATH, - sheet="TEAMS" - ) + src = _load_buf() - # Оставляем только полезные поля (подгони под свой файл) - keep = [ "Team", "Logo", "Short", "HexPodl", "HexBase", "HexText" ] - keep = [c for c in keep if c in teams_df.columns] - teams_df = teams_df.loc[:, keep].copy() + if format == "xlsx": + # читаем нужный лист из исходного XLSM + df = pd.read_excel(src, sheet_name=sheet, engine="openpyxl") - # 4) Нормализованные ключи для джоина по имени - teams_df["__key"] = teams_df["Team"].apply(_norm_name) + # пишем НОВЫЙ XLSX (без макросов) — это то, что понимает vMix + out = io.BytesIO() + with pd.ExcelWriter(out, engine="openpyxl") as writer: + df.to_excel(writer, sheet_name=sheet, index=False) + out.seek(0) - def _pick_team_info(name: str) -> dict: - key = _norm_name(name) - hit = teams_df.loc[teams_df["__key"] == key] - if hit.empty: - # не нашли точное совпадение — вернём только название - return {"Team": name} - rec = hit.iloc[0].to_dict() - rec.pop("__key", None) - # заменим NaN/inf на None, чтобы JSON не падал - for k, v in list(rec.items()): - if pd.isna(v) or v in (np.inf, -np.inf): - rec[k] = None - return rec + return StreamingResponse( + out, + media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + headers={ + # стабильное имя файла, чтобы vMix не путался + "Content-Disposition": "inline; filename=vmix.xlsx", + # отключаем кэш браузера/прокси, vMix сам опрашивает по интервалу + "Cache-Control": "no-cache, no-store, must-revalidate", + "Pragma": "no-cache", + }, + ) - home_info = _pick_team_info(home_name) - away_info = _pick_team_info(away_name) - date_obj = datetime.strptime(row.get("datetime_str", ""), "%d.%m.%Y %H:%M") - try: - full_format = date_obj.strftime("%B %-d, %Y") - except ValueError: - full_format = date_obj.strftime("%B %#d, %Y") + elif format == "csv": + df = pd.read_excel(src, sheet_name=sheet, engine="openpyxl") + csv_bytes = df.to_csv(index=False, encoding="utf-8-sig").encode("utf-8") + return StreamingResponse( + io.BytesIO(csv_bytes), + media_type="text/csv; charset=utf-8", + headers={ + "Content-Disposition": "inline; filename=vmix.csv", + "Cache-Control": "no-cache, no-store, must-revalidate", + "Pragma": "no-cache", + }, + ) + elif format == "json": + df = pd.read_excel(src, sheet_name=sheet, engine="openpyxl") + payload = json.dumps(df.to_dict(orient="records"), ensure_ascii=False) + return Response( + content=payload, + media_type="application/json; charset=utf-8", + headers={ + "Cache-Control": "no-cache, no-store, must-revalidate", + "Pragma": "no-cache", + }, + ) - payload = [{ - "selected_id": int(selected_game_id), - "tournament_id": int(current_tournament_id) if current_tournament_id else None, - "datetime": str(full_format), - "arena": str(row.get("arena_en", "")), - "arena_city": str(row.get("arena_city_en", "")), - "home": home_info, - "home_city": str(row.get("homeCity_en", "")), - "away": away_info, - "away_city": str(row.get("visitorCity_en", "")), - "season": current_season, - }] + return Response("Unsupported format", status_code=400) + + # # Оставляем только полезные поля (подгони под свой файл) + # keep = ["Team", "Logo", "Short", "HexPodl", "HexBase", "HexText"] + # keep = [c for c in keep if c in teams_df.columns] + # teams_df = teams_df.loc[:, keep].copy() + + # # 4) Нормализованные ключи для джоина по имени + # teams_df["__key"] = teams_df["Team"].apply(_norm_name) + + # def _pick_team_info(name: str) -> dict: + # key = _norm_name(name) + # hit = teams_df.loc[teams_df["__key"] == key] + # if hit.empty: + # # не нашли точное совпадение — вернём только название + # return {"Team": name} + # rec = hit.iloc[0].to_dict() + # rec.pop("__key", None) + # # заменим NaN/inf на None, чтобы JSON не падал + # for k, v in list(rec.items()): + # if pd.isna(v) or v in (np.inf, -np.inf): + # rec[k] = None + # return rec + + # home_info = _pick_team_info(home_name) + # away_info = _pick_team_info(away_name) + # date_obj = datetime.strptime(row.get("datetime_str", ""), "%d.%m.%Y %H:%M") + # try: + # full_format = date_obj.strftime("%B %-d, %Y") + # except ValueError: + # full_format = date_obj.strftime("%B %#d, %Y") + + # payload = [ + # { + # "selected_id": int(selected_game_id), + # "tournament_id": ( + # int(current_tournament_id) if current_tournament_id else None + # ), + # "datetime": str(full_format), + # "arena": str(row.get("arena_en", "")), + # "arena_city": str(row.get("arena_city_en", "")), + # "home": home_info, + # "home_city": str(row.get("homeCity_en", "")), + # "away": away_info, + # "away_city": str(row.get("visitorCity_en", "")), + # "season": current_season, + # } + # ] + + # return JSONResponse(content=payload) + # except Exception as ex: + # pprint(ex) - return JSONResponse(content=payload) if __name__ == "__main__": uvicorn.run( From 216138ceede0557ff96d29916d915eb0d43db1b9 Mon Sep 17 00:00:00 2001 From: Alexey Barabanov Date: Thu, 13 Nov 2025 15:09:50 +0300 Subject: [PATCH 2/5] =?UTF-8?q?=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=B2=20=D1=81=D0=BE=D0=B4=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B8=20=D1=81=D0=B5=D1=80=D0=B2=D0=B8=D1=81=D0=B0.=20=D0=94?= =?UTF-8?q?=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D1=8B:=20=D0=9B?= =?UTF-8?q?=D0=B8=D0=BC=D0=B8=D1=82=D1=8B=20=D0=9B=D0=BE=D0=B3=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20=D0=9F=D0=BE=D0=B2=D0=B5?= =?UTF-8?q?=D0=B4=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D1=80=D0=B8=20=D0=BF?= =?UTF-8?q?=D0=B5=D1=80=D0=B5=D0=B7=D0=B0=D0=BF=D1=83=D1=81=D0=BA=D0=B5=20?= =?UTF-8?q?=D0=A0=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=20=D1=81=20=D0=B2=D0=B8?= =?UTF-8?q?=D1=80=D1=82=D1=83=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B=D0=BC=20=D0=BE?= =?UTF-8?q?=D0=BA=D1=80=D1=8B=D0=B6=D0=B5=D0=BD=D0=B8=D0=B5=D0=BC=20=D0=B8?= =?UTF-8?q?=20=D1=84=D0=B0=D0=B9=D0=BB=D0=BE=D0=BC=20=D0=BE=D0=BA=D1=80?= =?UTF-8?q?=D1=83=D0=B6=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- deploy.sh | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/deploy.sh b/deploy.sh index 0ac9095..4510663 100755 --- a/deploy.sh +++ b/deploy.sh @@ -11,6 +11,7 @@ NC='\033[0m' # No Color REPO_URL="https://git.tvstart.ru/ychernenko/KHL.git" TARGET_DIR="/root/KHL" SERVICE_NAME="khl-data.service" +TARGET_ENV="/mnt/aescript/.env" show_help() { echo "Использование: $0 -r <релиз> [-h]" @@ -206,25 +207,38 @@ create_systemd_service() { fi # Формируем команду для data сервиса - local data_command="$TARGET_DIR/.venv/bin/python3 $TARGET_DIR/get_data.py" local data_service_file="/etc/systemd/system/$SERVICE_NAME" log_info "Создание файла сервиса: $data_service_file" cat > "$data_service_file" << EOF [Unit] Description=KHL Data Service +Documentation=https://git.tvstart.ru/ychernenko/KHL After=network.target +Wants=network.target [Service] Type=simple User=root WorkingDirectory=$TARGET_DIR Environment=PATH=$TARGET_DIR/.venv/bin -ExecStart=$data_command -Restart=always -RestartSec=30 +EnvironmentFile=$TARGET_ENV +ExecStart=python3 $TARGET_DIR/get_data.py + +# Лимиты ресурсов +MemoryMax=1G +CPUQuota=80% + +# Логирование StandardOutput=journal StandardError=journal +SyslogIdentifier=RFB + +# Поведение при перезапуске +Restart=always +RestartSec=10 +StartLimitInterval=300 +StartLimitBurst=5 [Install] WantedBy=multi-user.target From d4d9584bb30f1ce941295fa9d512929ec58f1663 Mon Sep 17 00:00:00 2001 From: Alexey Barabanov Date: Thu, 13 Nov 2025 15:28:14 +0300 Subject: [PATCH 3/5] =?UTF-8?q?=D0=9F=D0=BE=D0=BC=D0=B5=D0=BD=D1=8F=D0=BB?= =?UTF-8?q?=D0=B8=20=D0=B2=D0=B7=D0=BE=D0=B8=D0=BC=D0=BE=D0=B6=D0=B5=D0=B9?= =?UTF-8?q?=D1=81=D1=82=D0=B2=D0=B8=D0=B5=20=D1=81=20=D1=84=D0=B0=D0=B9?= =?UTF-8?q?=D0=BB=D0=BE=D0=BC=20=D0=BE=D0=BA=D1=80=D1=83=D0=B6=D0=B5=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F.=20=D0=9F=D0=BE=D0=BC=D0=B5=D0=BD=D1=8F=D0=BB?= =?UTF-8?q?=D0=B8=20=D1=87=D1=82=D0=BE=20=D0=B1=D0=B5=D1=80=D0=B5=D1=82?= =?UTF-8?q?=D1=8C=D1=81=D1=8F=20=D0=B8=D0=B7=20=D1=84=D0=B0=D0=B9=D0=BB?= =?UTF-8?q?=D0=B0=20=D0=BE=D0=BA=D1=80=D1=83=D0=B6=D0=B5=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- get_data.py | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/get_data.py b/get_data.py index 780b23f..1cc36d6 100644 --- a/get_data.py +++ b/get_data.py @@ -29,27 +29,21 @@ _latest_lock = Lock() _stop_event = Event() _worker_thread: Thread | None = None -# Загружаем переменные из .env -if load_dotenv(dotenv_path="/mnt/khl/.env", verbose=True): - print("Добавить в лог что был найден файл окружения!!") - pass -else: - load_dotenv() - print("Добавить в лог что не был найден файл окружения!!") - +pprint(f"Локальный файл окружения ={load_dotenv(verbose=True)}") api_user = os.getenv("API_USER") api_pass = os.getenv("API_PASS") league = os.getenv("LEAGUE") +api_base_url = os.getenv("API_BASE_URL") POLL_SEC = int(os.getenv("GAME_POLL_SECONDS")) SERVER_NAME = os.getenv("SYNO_URL") USER = os.getenv("SYNO_USERNAME") PASSWORD = os.getenv("SYNO_PASSWORD") -PATH = "/team-folders/GFX/Hockey/KHL/Soft/MATCH.xlsm" +PATH = f'{os.getenv("SYNO_PATH")}MATCH.xlsm' def load_today_schedule(): """Возвращает DataFrame матчей на сегодня с нужными колонками (или пустой DF).""" - url_tournaments = "http://stat2tv.khl.ru/tournaments.xml" + url_tournaments = f"{api_base_url}tournaments.xml" r = requests.get( url_tournaments, auth=HTTPBasicAuth(api_user, api_pass), verify=False ) @@ -73,7 +67,7 @@ def load_today_schedule(): global current_tournament_id, current_season current_tournament_id = tournament_id current_season = season - url_schedule = f"http://stat2tv.khl.ru/{tournament_id}/schedule-{tournament_id}.xml" + url_schedule = f"{api_base_url}{tournament_id}/schedule-{tournament_id}.xml" r = requests.get(url_schedule, auth=HTTPBasicAuth(api_user, api_pass), verify=False) schedule_df = pd.read_xml(io.StringIO(r.text)) @@ -131,7 +125,7 @@ def load_today_schedule(): def _build_game_url(tournament_id: int, game_id: int) -> str: # URL по аналогии с расписанием: .../{tournament_id}/json_en/{game_id}.json # Если у тебя другой шаблон — просто поменяй строку ниже. - return f"http://stat2tv.khl.ru/{tournament_id}/json_en/{game_id}.json" + return f"{api_base_url}{tournament_id}/json_en/{game_id}.json" def _fetch_game_once(tournament_id: int, game_id: int) -> dict: From 9cbf32f831162be2d277eac410f718aceebce6f9 Mon Sep 17 00:00:00 2001 From: Alexey Barabanov Date: Thu, 13 Nov 2025 15:30:09 +0300 Subject: [PATCH 4/5] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=BE=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BE=D0=BF=D0=B5=D1=87=D0=B0=D1=82?= =?UTF-8?q?=D0=BA=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++++ deploy.sh | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e69de29..800cca9 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,4 @@ +## Проверка логов +```shell +sudo journalctl -t AFS +``` \ No newline at end of file diff --git a/deploy.sh b/deploy.sh index 4510663..e063b95 100755 --- a/deploy.sh +++ b/deploy.sh @@ -232,7 +232,7 @@ CPUQuota=80% # Логирование StandardOutput=journal StandardError=journal -SyslogIdentifier=RFB +SyslogIdentifier=KHL # Поведение при перезапуске Restart=always From c60caaa8aaad763cf7605dcd2c4502b8dfc3be84 Mon Sep 17 00:00:00 2001 From: Alexey Barabanov Date: Thu, 13 Nov 2025 15:30:27 +0300 Subject: [PATCH 5/5] Jgtxfnrf --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 800cca9..e7111d5 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ ## Проверка логов ```shell -sudo journalctl -t AFS +sudo journalctl -t KHL ``` \ No newline at end of file