добавлена библиотека nasio
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,4 +1,5 @@
|
|||||||
.env
|
.env
|
||||||
/.venv
|
/.venv
|
||||||
/logs/*
|
/logs/*
|
||||||
/__pycache__
|
/__pycache__/*
|
||||||
|
*.pyc
|
||||||
Binary file not shown.
Binary file not shown.
41
function.py
41
function.py
@@ -1,41 +0,0 @@
|
|||||||
import os
|
|
||||||
from io import BytesIO
|
|
||||||
import pandas as pd
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
from synology_drive_api.drive import SynologyDrive
|
|
||||||
from synology_drive_api.base import SynologyException
|
|
||||||
|
|
||||||
load_dotenv()
|
|
||||||
|
|
||||||
SYNO_HOST = os.getenv("SYNO_BASE_URL", "").strip() # без схемы! например: "nas.example.com"
|
|
||||||
SYNO_USERNAME = os.getenv("SYNO_USERNAME", "")
|
|
||||||
SYNO_PASSWORD = os.getenv("SYNO_PASSWORD", "")
|
|
||||||
SYNO_PORT = int(os.getenv("SYNO_PORT", "5001"))
|
|
||||||
|
|
||||||
def load_osheet(file_path: str, sheet: str | int = 0) -> pd.DataFrame:
|
|
||||||
"""
|
|
||||||
Скачивает Synology Office .osheet через Drive API (делает экспорт в xlsx под капотом)
|
|
||||||
и возвращает pandas.DataFrame для указанного листа.
|
|
||||||
file_path: путь вида '/GFX/…/MATCH.osheet' (шара должна быть Team Folder в Drive).
|
|
||||||
"""
|
|
||||||
|
|
||||||
drv = SynologyDrive(SYNO_USERNAME, SYNO_PASSWORD, SYNO_HOST, SYNO_PORT, https=True, dsm_version="7")
|
|
||||||
try:
|
|
||||||
# вернёт BytesIO c xlsx
|
|
||||||
bio = drv.download_synology_office_file(file_path)
|
|
||||||
df = pd.read_excel(bio, sheet_name=sheet)
|
|
||||||
return df
|
|
||||||
except SynologyException as e:
|
|
||||||
# Часто: {"code":1000,"message":"get file information failed"} — Drive не видит путь (нет Team Folder/прав)
|
|
||||||
raise RuntimeError(
|
|
||||||
"Не удалось экспортировать .osheet через Synology Drive. "
|
|
||||||
"Проверь, что шара включена в Drive Admin Console → Team Folder, "
|
|
||||||
"и у пользователя есть права. Исходная ошибка: " + str(e)
|
|
||||||
)
|
|
||||||
finally:
|
|
||||||
try:
|
|
||||||
drv.logout()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
load_osheet(file_path="/GFX/Hockey/KHL/Soft/MATCH.osheet", sheet="TEAMS")
|
|
||||||
47
get_data.py
47
get_data.py
@@ -10,8 +10,8 @@ import uvicorn
|
|||||||
from threading import Thread, Event, Lock
|
from threading import Thread, Event, Lock
|
||||||
import time
|
import time
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
#from smb_excel import read_excel_from_smb, fetch_smb_file
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import nasio
|
||||||
|
|
||||||
|
|
||||||
# --- Глобальные переменные ---
|
# --- Глобальные переменные ---
|
||||||
@@ -36,13 +36,10 @@ api_user = os.getenv("API_USER")
|
|||||||
api_pass = os.getenv("API_PASS")
|
api_pass = os.getenv("API_PASS")
|
||||||
league = os.getenv("LEAGUE")
|
league = os.getenv("LEAGUE")
|
||||||
POLL_SEC = int(os.getenv("GAME_POLL_SECONDS"))
|
POLL_SEC = int(os.getenv("GAME_POLL_SECONDS"))
|
||||||
SERVER_NAME = os.getenv("SERVER_NAME")
|
SERVER_NAME = os.getenv("SYNO_URL")
|
||||||
SERVER_IP = os.getenv("SERVER_IP")
|
USER = os.getenv("SYNO_USERNAME")
|
||||||
SHARE = os.getenv("SHARE")
|
PASSWORD = os.getenv("SYNO_PASSWORD")
|
||||||
PATH_IN_SHARE = os.getenv("PATH_IN_SHARE")
|
PATH = "/team-folders/GFX/Hockey/KHL/Soft/MATCH.xlsm"
|
||||||
USER = os.getenv("USER")
|
|
||||||
PASSWORD = os.getenv("PASSWORD")
|
|
||||||
DOMAIN = os.getenv("DOMAIN")
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -652,25 +649,6 @@ def _build_all_stats(payload: dict) -> dict:
|
|||||||
})
|
})
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def xl():
|
|
||||||
df = read_excel_from_smb(
|
|
||||||
server_name=SERVER_NAME,
|
|
||||||
server_ip=SERVER_IP,
|
|
||||||
share_name=SHARE,
|
|
||||||
file_path_in_share=PATH_IN_SHARE,
|
|
||||||
username=USER,
|
|
||||||
password=PASSWORD,
|
|
||||||
domain=DOMAIN,
|
|
||||||
client_machine_name="KHL_SOFT",
|
|
||||||
sheet_name="TEAMS", # или None, или список листов
|
|
||||||
)
|
|
||||||
df = df.replace([float("inf"), float("-inf")], pd.NA)
|
|
||||||
columns_to_keep = [
|
|
||||||
"Team", "Logo", "Short", "HexPodl", # ← укажи свои
|
|
||||||
]
|
|
||||||
df = df.loc[:, [c for c in columns_to_keep if c in df.columns]]
|
|
||||||
json_text = df.to_json(orient="records", force_ascii=False) # NaN/NA -> null
|
|
||||||
return Response(content=json_text, media_type="application/json; charset=utf-8")
|
|
||||||
|
|
||||||
@app.get("/teams/stats")
|
@app.get("/teams/stats")
|
||||||
async def teams_stats(
|
async def teams_stats(
|
||||||
@@ -756,16 +734,13 @@ async def info():
|
|||||||
away_name = str(row.get("visitorName_en", "")).strip()
|
away_name = str(row.get("visitorName_en", "")).strip()
|
||||||
|
|
||||||
# 3) Подтягиваем справочник команд из Excel (лист TEAMS)
|
# 3) Подтягиваем справочник команд из Excel (лист TEAMS)
|
||||||
teams_df = read_excel_from_smb(
|
teams_df = nasio.load_formatted(
|
||||||
server_name=SERVER_NAME,
|
user=USER,
|
||||||
server_ip=SERVER_IP,
|
|
||||||
share_name=SHARE,
|
|
||||||
file_path_in_share=PATH_IN_SHARE,
|
|
||||||
username=USER,
|
|
||||||
password=PASSWORD,
|
password=PASSWORD,
|
||||||
domain=DOMAIN,
|
nas_ip=SERVER_NAME,
|
||||||
client_machine_name="KHL_SOFT",
|
nas_port="443",
|
||||||
sheet_name="TEAMS",
|
path=PATH,
|
||||||
|
sheet="TEAMS"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Оставляем только полезные поля (подгони под свой файл)
|
# Оставляем только полезные поля (подгони под свой файл)
|
||||||
|
|||||||
@@ -8,3 +8,5 @@ python-telegram-handler
|
|||||||
python-dotenv>=1.1.0
|
python-dotenv>=1.1.0
|
||||||
lxml
|
lxml
|
||||||
python-dotenv
|
python-dotenv
|
||||||
|
--extra-index-url https://git.tvstart.ru/api/packages/lexx/pypi/simple
|
||||||
|
nasio
|
||||||
154
smb_excel.py
154
smb_excel.py
@@ -1,154 +0,0 @@
|
|||||||
# smb_excel.py (обновлённый фрагмент)
|
|
||||||
from io import BytesIO
|
|
||||||
from typing import Optional, Union, Any, Dict
|
|
||||||
#from smb.SMBConnection import SMBConnection
|
|
||||||
import pandas as pd
|
|
||||||
import re
|
|
||||||
|
|
||||||
class SMBDownloadError(Exception):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def _normalize_path(p: str) -> str:
|
|
||||||
"""Убирает случайные r"..."/"...", лишние кавычки и ставит прямые слэши."""
|
|
||||||
if p is None:
|
|
||||||
return p
|
|
||||||
# уберём обрамляющие кавычки/префикс r"..."
|
|
||||||
m = re.fullmatch(r"""r?["'](.*)["']""", p.strip())
|
|
||||||
if m:
|
|
||||||
p = m.group(1)
|
|
||||||
# заменим \ на /
|
|
||||||
p = p.replace("\\", "/").lstrip("/") # путь внутри шары не должен начинаться с /
|
|
||||||
return p
|
|
||||||
|
|
||||||
def _try_connect(
|
|
||||||
remote_name: str,
|
|
||||||
server_ip: str,
|
|
||||||
username: str,
|
|
||||||
password: str,
|
|
||||||
*,
|
|
||||||
client_machine_name: str,
|
|
||||||
domain: str,
|
|
||||||
port: int
|
|
||||||
) -> Optional[SMBConnection]:
|
|
||||||
conn = SMBConnection(
|
|
||||||
username,
|
|
||||||
password,
|
|
||||||
client_machine_name,
|
|
||||||
remote_name, # ВАЖНО: remote_name — имя сервера (или IP как fallback)
|
|
||||||
domain=domain,
|
|
||||||
use_ntlm_v2=True,
|
|
||||||
is_direct_tcp=True
|
|
||||||
)
|
|
||||||
try:
|
|
||||||
if conn.connect(server_ip, port):
|
|
||||||
return conn
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
return None
|
|
||||||
|
|
||||||
def _connect_smb(
|
|
||||||
server_name: str,
|
|
||||||
server_ip: str,
|
|
||||||
username: str,
|
|
||||||
password: str,
|
|
||||||
*,
|
|
||||||
client_machine_name: str = "client",
|
|
||||||
domain: str = "",
|
|
||||||
port: int = 445,
|
|
||||||
) -> SMBConnection:
|
|
||||||
"""
|
|
||||||
Пробуем два варианта remote_name: (1) реальное имя сервера, (2) IP.
|
|
||||||
"""
|
|
||||||
for remote_name in (server_name, server_ip):
|
|
||||||
if not remote_name:
|
|
||||||
continue
|
|
||||||
conn = _try_connect(
|
|
||||||
remote_name=remote_name,
|
|
||||||
server_ip=server_ip,
|
|
||||||
username=username,
|
|
||||||
password=password,
|
|
||||||
client_machine_name=client_machine_name,
|
|
||||||
domain=domain,
|
|
||||||
port=port,
|
|
||||||
)
|
|
||||||
if conn:
|
|
||||||
return conn
|
|
||||||
raise SMBDownloadError(
|
|
||||||
f"Не удалось подключиться к SMB {server_ip}:{port}. "
|
|
||||||
f"Проверь server_name (реальное имя хоста), домен/логин и доступность порта 445."
|
|
||||||
)
|
|
||||||
|
|
||||||
def fetch_smb_file(
|
|
||||||
*,
|
|
||||||
server_name: str,
|
|
||||||
server_ip: str,
|
|
||||||
share_name: str,
|
|
||||||
file_path_in_share: str,
|
|
||||||
username: str,
|
|
||||||
password: str,
|
|
||||||
client_machine_name: str = "client",
|
|
||||||
domain: str = "",
|
|
||||||
port: int = 445,
|
|
||||||
) -> BytesIO:
|
|
||||||
"""
|
|
||||||
Возвращает содержимое файла из SMB как BytesIO.
|
|
||||||
"""
|
|
||||||
file_path_in_share = _normalize_path(file_path_in_share)
|
|
||||||
conn: Optional[SMBConnection] = None
|
|
||||||
try:
|
|
||||||
conn = _connect_smb(
|
|
||||||
server_name=server_name,
|
|
||||||
server_ip=server_ip,
|
|
||||||
username=username,
|
|
||||||
password=password,
|
|
||||||
client_machine_name=client_machine_name,
|
|
||||||
domain=domain,
|
|
||||||
port=port,
|
|
||||||
)
|
|
||||||
# Быстрая проверка существования каталога/файла
|
|
||||||
parent = "/".join(file_path_in_share.split("/")[:-1]) or ""
|
|
||||||
# listPath кидает исключение, если каталога/шары нет — получим понятную ошибку
|
|
||||||
if parent:
|
|
||||||
conn.listPath(share_name, parent)
|
|
||||||
|
|
||||||
buf = BytesIO()
|
|
||||||
conn.retrieveFile(share_name, file_path_in_share, buf)
|
|
||||||
buf.seek(0)
|
|
||||||
return buf
|
|
||||||
except Exception as e:
|
|
||||||
raise SMBDownloadError(
|
|
||||||
f"Ошибка скачивания {share_name}/{file_path_in_share}: {e}"
|
|
||||||
) from e
|
|
||||||
finally:
|
|
||||||
if conn:
|
|
||||||
try:
|
|
||||||
conn.close()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def read_excel_from_smb(
|
|
||||||
*,
|
|
||||||
server_name: str,
|
|
||||||
server_ip: str,
|
|
||||||
share_name: str,
|
|
||||||
file_path_in_share: str,
|
|
||||||
username: str,
|
|
||||||
password: str,
|
|
||||||
client_machine_name: str = "client",
|
|
||||||
domain: str = "",
|
|
||||||
port: int = 445,
|
|
||||||
sheet_name: Optional[Union[str, int, list]] = None,
|
|
||||||
**read_excel_kwargs: Dict[str, Any],
|
|
||||||
) -> pd.DataFrame:
|
|
||||||
file_obj = fetch_smb_file(
|
|
||||||
server_name=server_name,
|
|
||||||
server_ip=server_ip,
|
|
||||||
share_name=share_name,
|
|
||||||
file_path_in_share=file_path_in_share,
|
|
||||||
username=username,
|
|
||||||
password=password,
|
|
||||||
client_machine_name=client_machine_name,
|
|
||||||
domain=domain,
|
|
||||||
port=port,
|
|
||||||
)
|
|
||||||
return pd.read_excel(file_obj, sheet_name=sheet_name, **read_excel_kwargs)
|
|
||||||
Reference in New Issue
Block a user