Compare commits
96 Commits
139740b48f
...
RFB4
| Author | SHA1 | Date | |
|---|---|---|---|
| 26eb5a9398 | |||
| a97a28de6f | |||
| 764c50e42d | |||
| d49244de54 | |||
| a9605be520 | |||
| 763df5cb43 | |||
| 662f88c68b | |||
| c438f69578 | |||
| 9bf3624c8c | |||
| 7b44ab3d5d | |||
| 6721951c34 | |||
| ea34b6521f | |||
| 5ab179e47b | |||
| 1839b8aec3 | |||
| f11b315d2f | |||
| 15367b05fc | |||
| 94d487fe88 | |||
| 049e2493b5 | |||
| 7e5ccdbb83 | |||
| 97045a0f72 | |||
| b301f1e918 | |||
| d86d1e5a4f | |||
| 6278a8e3df | |||
| 4ad11815a5 | |||
| cbcad5e525 | |||
| 48c5f552ec | |||
| b0511d51d3 | |||
| 6fa1ad3f35 | |||
| d821967f33 | |||
| b11dbdeae8 | |||
| 91c6eeddc5 | |||
| 842af85847 | |||
| 225e158c53 | |||
| 276ecdcb85 | |||
| adc067293f | |||
| 27a7ffcaad | |||
| 02582bdab0 | |||
| 4048f8fdfa | |||
| b0aba27764 | |||
| b27e02bb49 | |||
| ff3883aa36 | |||
| f72914a085 | |||
| 10baa5b2fb | |||
| 6a06d54aa4 | |||
| 58bfdce841 | |||
| 9fa68a165d | |||
| ee58ad868d | |||
| 5470df8da9 | |||
| 8fa3acf3f2 | |||
| 858ed5c6a7 | |||
| 09dfbef09a | |||
| c4de3c84fe | |||
| 000b304ed9 | |||
| 8f34737186 | |||
| 58bff8ba53 | |||
| 82f1450ff5 | |||
| 7a61c0bfb6 | |||
| d75f0e6407 | |||
| dcfcbb0958 | |||
| 629854c104 | |||
| 893d18ee23 | |||
| c9613f91a3 | |||
| 5ea03d2efc | |||
| bacee0e9b8 | |||
| 0844d577ff | |||
| d7c819357d | |||
| efa4812b09 | |||
| c62c8a31a6 | |||
| a13cb945c7 | |||
| 5083423660 | |||
| c30bc088c7 | |||
| d9ad941a80 | |||
| 84169208ab | |||
| 39ba101948 | |||
| 886339b183 | |||
| 00a01eb8a9 | |||
| 313c82c006 | |||
| b45d7ec129 | |||
| 8b2f2f5e23 | |||
| b021034b4a | |||
| 963c7a503b | |||
| e375613692 | |||
| 0b85081ba5 | |||
| b8b79b5f58 | |||
| 6d3d5d3702 | |||
| 9b0c276d36 | |||
| b0f59e5bf3 | |||
| 5cb7b2f6c8 | |||
| a1b1466f4b | |||
| dbc6ca3ef9 | |||
| 856ca094b1 | |||
| 1b13768d12 | |||
| c3469b62ef | |||
| 6e104a565c | |||
| 2e517607e4 | |||
| aa62821e5f |
6
.gitignore
vendored
6
.gitignore
vendored
@@ -1,4 +1,8 @@
|
||||
.venv/*
|
||||
/JSON/*
|
||||
/logs/*
|
||||
/static/*
|
||||
/static/*
|
||||
get_data_new copy 2.py
|
||||
get_data_new copy.py
|
||||
temp.json
|
||||
get_data_new copy 3.py
|
||||
5
.streamlit/config.toml
Normal file
5
.streamlit/config.toml
Normal file
@@ -0,0 +1,5 @@
|
||||
[browser]
|
||||
gatherUsageStats = false
|
||||
|
||||
[server]
|
||||
enableStaticServing = true
|
||||
302
PlayTypeID.json
Normal file
302
PlayTypeID.json
Normal file
@@ -0,0 +1,302 @@
|
||||
[
|
||||
{
|
||||
"PlayTypeID": 0,
|
||||
"PlayInfoSite": "-",
|
||||
"PlayInfo": "Пустое событие"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 1,
|
||||
"PlayInfoSite": "1 очко",
|
||||
"PlayInfo": "Штрафной бросок - попадание"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 2,
|
||||
"PlayInfoSite": "2 очка",
|
||||
"PlayInfo": "2х очковый бросок - попадание"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 3,
|
||||
"PlayInfoSite": "3 очка",
|
||||
"PlayInfo": "3х очковый бросок - попадание"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 4,
|
||||
"PlayInfoSite": "Мимо 1 очко",
|
||||
"PlayInfo": "Штрафной бросок - промах"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 5,
|
||||
"PlayInfoSite": "Мимо 2 очка",
|
||||
"PlayInfo": "2х очковый бросок - промах"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 6,
|
||||
"PlayInfoSite": "Мимо 3 очка",
|
||||
"PlayInfo": "3х очковый бросок - промах"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 7,
|
||||
"PlayInfoSite": "Замена",
|
||||
"PlayInfo": "Замена игроков"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 8,
|
||||
"PlayInfoSite": "Выход",
|
||||
"PlayInfo": "Выход на площадку"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 9,
|
||||
"PlayInfoSite": "Уход",
|
||||
"PlayInfo": "Ушел с площадки"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 10,
|
||||
"PlayInfoSite": "Потеря",
|
||||
"PlayInfo": "Потеря"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 11,
|
||||
"PlayInfoSite": "Пас-потеря",
|
||||
"PlayInfo": "Потеря мяча при передаче"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 12,
|
||||
"PlayInfoSite": "Пробежка",
|
||||
"PlayInfo": "Пробежка"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 13,
|
||||
"PlayInfoSite": "Заступ в аут",
|
||||
"PlayInfo": "Заступ в аут"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 14,
|
||||
"PlayInfoSite": "Зона",
|
||||
"PlayInfo": "Зона"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 15,
|
||||
"PlayInfoSite": "Двойное ведение",
|
||||
"PlayInfo": "Двойное ведение"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 16,
|
||||
"PlayInfoSite": "3 секунды",
|
||||
"PlayInfo": "3 секунды"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 17,
|
||||
"PlayInfoSite": "5 секунд",
|
||||
"PlayInfo": "5 секунд"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 18,
|
||||
"PlayInfoSite": "8 секунд",
|
||||
"PlayInfo": "8 секунд"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 19,
|
||||
"PlayInfoSite": "24 секунды",
|
||||
"PlayInfo": "24 секунды"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 20,
|
||||
"PlayInfoSite": "Потеря мяча",
|
||||
"PlayInfo": "Потеря мяча"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 21,
|
||||
"PlayInfoSite": "Начало четверти",
|
||||
"PlayInfo": "Начало четверти?"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 22,
|
||||
"PlayInfoSite": "",
|
||||
"PlayInfo": "Окончание четверти?"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 23,
|
||||
"PlayInfoSite": "Тайм-аут",
|
||||
"PlayInfo": "Тайм-аут"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 24,
|
||||
"PlayInfoSite": "",
|
||||
"PlayInfo": "неизвестно"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 25,
|
||||
"PlayInfoSite": "Пас",
|
||||
"PlayInfo": "Передача"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 26,
|
||||
"PlayInfoSite": "Перехват",
|
||||
"PlayInfo": "Перехват"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 27,
|
||||
"PlayInfoSite": "Блокшот",
|
||||
"PlayInfo": "Блокшот"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 28,
|
||||
"PlayInfoSite": "Подбор",
|
||||
"PlayInfo": "Подбор"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 29,
|
||||
"PlayInfoSite": "Быстрый отрыв",
|
||||
"PlayInfo": "Быстрый отрыв"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 30,
|
||||
"PlayInfoSite": "Бросок сверху",
|
||||
"PlayInfo": "Тип броска"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 31,
|
||||
"PlayInfoSite": "В прыжке",
|
||||
"PlayInfo": "Тип броска"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 32,
|
||||
"PlayInfoSite": "В проходе",
|
||||
"PlayInfo": "Тип броска"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 33,
|
||||
"PlayInfoSite": "Добивание",
|
||||
"PlayInfo": "Тип броска"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 34,
|
||||
"PlayInfoSite": "Навес",
|
||||
"PlayInfo": "Тип броска"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 35,
|
||||
"PlayInfoSite": "Бросок крюком",
|
||||
"PlayInfo": "Тип броска"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 40,
|
||||
"PlayInfoSite": "Фол P",
|
||||
"PlayInfo": "Фол персональный"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 41,
|
||||
"PlayInfoSite": "Фол U",
|
||||
"PlayInfo": "Фол неспортивный"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 42,
|
||||
"PlayInfoSite": "Фол T",
|
||||
"PlayInfo": "Фол технический"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 43,
|
||||
"PlayInfoSite": "Фол D",
|
||||
"PlayInfo": "Фол дисквалифицирующий"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 44,
|
||||
"PlayInfoSite": "Фол C",
|
||||
"PlayInfo": "Фол технический главному тренеру за его личное неспортивное поведение"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 45,
|
||||
"PlayInfoSite": "Фол В",
|
||||
"PlayInfo": "Фол технический главному тренеру по любой другой причине"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 47,
|
||||
"PlayInfoSite": "В нападении",
|
||||
"PlayInfo": "Тип фола"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 50,
|
||||
"PlayInfoSite": "Вбрасывание",
|
||||
"PlayInfo": "Тип фола"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 51,
|
||||
"PlayInfoSite": "1 бросок",
|
||||
"PlayInfo": "Назначение 1го штрафного броска"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 52,
|
||||
"PlayInfoSite": "2 броска",
|
||||
"PlayInfo": "Назначение 2х штрафных бросков"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 53,
|
||||
"PlayInfoSite": "3 броска",
|
||||
"PlayInfo": "Назначение 3х штрафных бросков"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 54,
|
||||
"PlayInfoSite": "Компенсация",
|
||||
"PlayInfo": "Обоюдное пробивание штрафных"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 59,
|
||||
"PlayInfoSite": "",
|
||||
"PlayInfo": "пробел для сайта"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 60,
|
||||
"PlayInfoSite": "Разминка команд",
|
||||
"PlayInfo": "Разминка команд"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 61,
|
||||
"PlayInfoSite": "Представление команд",
|
||||
"PlayInfo": "Представление команд"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 62,
|
||||
"PlayInfoSite": "Менее 3 минут до начала игры",
|
||||
"PlayInfo": "Менее 3 минут до начала игры"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 63,
|
||||
"PlayInfoSite": "Игра сейчас начнется",
|
||||
"PlayInfo": "Игра сейчас начнется"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 69,
|
||||
"PlayInfoSite": "Видеопросмотр",
|
||||
"PlayInfo": "Видеопросмотр?"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 70,
|
||||
"PlayInfoSite": "",
|
||||
"PlayInfo": "Возможно остановка игры?"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 71,
|
||||
"PlayInfoSite": "",
|
||||
"PlayInfo": "Возможно возобновление игры?"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 72,
|
||||
"PlayInfoSite": "Видеопросмотр",
|
||||
"PlayInfo": "Видеопросмотр?"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 73,
|
||||
"PlayInfoSite": "Старт видеотрансляции",
|
||||
"PlayInfo": "Старт видеотрансляции"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 74,
|
||||
"PlayInfoSite": "Конец видеотрансляции",
|
||||
"PlayInfo": "Конец видеотрансляции"
|
||||
},
|
||||
{
|
||||
"PlayTypeID": 100,
|
||||
"PlayInfoSite": "Матч завершен",
|
||||
"PlayInfo": "Матч завершен"
|
||||
}
|
||||
]
|
||||
105
README.md
Normal file
105
README.md
Normal file
@@ -0,0 +1,105 @@
|
||||

|
||||
|
||||
RFB Stat - это система для автоматического сбора баскетбольной статистики.
|
||||
Система состоит из двух основных компонентов:
|
||||
|
||||
Сервис данных (rfb-data.service) - сбор и обработка данных
|
||||
|
||||
Сервис визуализации (rfb-visual.service) - веб-интерфейс на основе Streamlit
|
||||
|
||||
Требования
|
||||
|
||||
* Ubuntu Linux
|
||||
* Доступ к репозиторию `https://git.tvstart.ru/`
|
||||
* *Права root для установки!!*
|
||||
|
||||
# Установка
|
||||
|
||||
Скрипт установки выполняет полную настройку системы:
|
||||
|
||||
```shell
|
||||
chmod +x deploy.sh
|
||||
./deploy.sh -t <команда> -r <релиз>
|
||||
```
|
||||
Параметры командной строки
|
||||
|
||||
-t - Название команды (опционально, определяется по IP если не указано)
|
||||
|
||||
-r - Релиз (ветка или тег в git, по умолчанию: main)
|
||||
|
||||
-h - Справка по использованию
|
||||
|
||||
# Примеры использования
|
||||
|
||||
Установка с автоопределением команды по IP
|
||||
```shell
|
||||
./deploy.sh -r main
|
||||
```
|
||||
Установка для конкретной команды
|
||||
```shell
|
||||
./deploy.sh -t cska -r main
|
||||
```
|
||||
Установка тестовой версии
|
||||
```shell
|
||||
./deploy.sh -t zenit -r Barabanov_TEST
|
||||
```
|
||||
# Пакеты и прочее что будет установленно:
|
||||
Системные пакеты:
|
||||
|
||||
* Python3
|
||||
* pip3
|
||||
* virtualenv
|
||||
* Git
|
||||
* Net-tools
|
||||
|
||||
Виртуальное окружение Python с зависимостями:
|
||||
|
||||
* streamlit
|
||||
* requests
|
||||
* pandas
|
||||
* numpy
|
||||
* plotly
|
||||
* watchdog
|
||||
* pillow
|
||||
* streamlit_autorefresh
|
||||
|
||||
# Systemd сервисы:
|
||||
|
||||
`rfb-data.service - сбор данных`
|
||||
|
||||
`rfb-visual.service - веб-интерфейс`
|
||||
|
||||
|
||||
# Управление сервисами
|
||||
Просмотр статуса обоих сервисов одновременно
|
||||
```shell
|
||||
systemctl status rfb-data.service rfb-visual.service
|
||||
```
|
||||
# Просмотр логов
|
||||
Логи сбора данных
|
||||
```shell
|
||||
journalctl -u rfb-data.service -f
|
||||
```
|
||||
Логи веб-интерфейса
|
||||
```shell
|
||||
journalctl -u rfb-visual.service -f
|
||||
```
|
||||
|
||||
# Управление сервисами
|
||||
Перезапуск всех сервисов
|
||||
```shell
|
||||
systemctl restart rfb-data.service rfb-visual.service
|
||||
```
|
||||
Остановка всех сервисов
|
||||
```shell
|
||||
systemctl stop rfb-data.service rfb-visual.service
|
||||
```
|
||||
Запуск всех сервисов
|
||||
```shell
|
||||
systemctl start rfb-data.service rfb-visual.service
|
||||
```
|
||||
# Доступ к приложению
|
||||
|
||||
После установки приложение доступно по адресу:
|
||||
|
||||
`http://ВАШ_IP_АДРЕС:8501`
|
||||
@@ -7,13 +7,15 @@ YELLOW='\033[1;33m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
show_help() {
|
||||
echo "Использование: $0 -t <домашняя команда> -r <релиз>"
|
||||
echo "Использование: $0 -t <домашняя команда> -r <релиз> [-l <лига>]"
|
||||
echo " -t Домашняя команда"
|
||||
echo " -r Релиз (тег или ветка в git)"
|
||||
echo " -l Лига (опционально)"
|
||||
echo " -h Показать эту справку"
|
||||
echo ""
|
||||
echo "Пример: $0 -t cska -r main"
|
||||
echo "Пример: $0 -t zenit -r Barabanov_TEST"
|
||||
echo "Пример: $0 -t zenit -r Barabanov_TEST -l vtb"
|
||||
echo "Пример: $0 -t avtodor -r main"
|
||||
echo ""
|
||||
exit 0
|
||||
}
|
||||
@@ -177,8 +179,12 @@ print('✓ Все основные пакеты успешно импортир
|
||||
# Функция создания systemd сервисов
|
||||
create_systemd_services() {
|
||||
local team="$1"
|
||||
local league="$2"
|
||||
|
||||
log_info "Создание отдельных systemd сервисов для команды: $team"
|
||||
if [ -n "$league" ]; then
|
||||
log_info "Лига: $league"
|
||||
fi
|
||||
|
||||
# Останавливаем и отключаем старые сервисы если они есть
|
||||
for service in rfb-data.service rfb-visual.service rfb-stat.service; do
|
||||
@@ -192,6 +198,12 @@ create_systemd_services() {
|
||||
fi
|
||||
done
|
||||
|
||||
# Формируем команду для data сервиса
|
||||
local data_command="/root/RFB/.venv/bin/python3 /root/RFB/get_data.py --team \"$team\""
|
||||
if [ -n "$league" ]; then
|
||||
data_command="$data_command --league \"$league\""
|
||||
fi
|
||||
|
||||
# Сервис для data
|
||||
local data_service_file="/etc/systemd/system/rfb-data.service"
|
||||
|
||||
@@ -205,7 +217,7 @@ Type=simple
|
||||
User=root
|
||||
WorkingDirectory=/root/RFB
|
||||
Environment=PATH=/root/RFB/.venv/bin
|
||||
ExecStart=/root/RFB/.venv/bin/python3 /root/RFB/get_data.py --team $team
|
||||
ExecStart=$data_command
|
||||
Restart=always
|
||||
RestartSec=10
|
||||
StandardOutput=journal
|
||||
@@ -290,14 +302,14 @@ detect_team() {
|
||||
# База данных хостов в виде пар IP-команда
|
||||
declare -A hosts=(
|
||||
["10.10.35.21"]="cska"
|
||||
["10.10.35.22"]="Kuban"
|
||||
["10.10.35.22"]="Lokomotiv Kuban"
|
||||
["10.10.35.23"]="uralmash"
|
||||
["10.10.35.24"]="betcity"
|
||||
["10.10.35.24"]="betcity parma"
|
||||
["10.10.35.25"]="avtodor"
|
||||
["10.10.35.26"]="zenit"
|
||||
["10.10.35.27"]="samara"
|
||||
["10.10.35.28"]="mba-mai"
|
||||
["10.10.35.29"]="Pari_Nizhny_Novgorod"
|
||||
["10.10.35.29"]="Pari Nizhny Novgorod"
|
||||
["10.10.35.30"]="unics"
|
||||
)
|
||||
|
||||
@@ -393,12 +405,14 @@ check_services_status() {
|
||||
main() {
|
||||
local team=""
|
||||
local release="main" # значение по умолчанию
|
||||
local league="" # новая переменная для лиги
|
||||
|
||||
# Обработка аргументов командной строки
|
||||
while getopts "t:r:h" opt; do
|
||||
while getopts "t:r:l:h" opt; do
|
||||
case $opt in
|
||||
t) team="$OPTARG" ;;
|
||||
r) release="$OPTARG" ;;
|
||||
l) league="$OPTARG" ;;
|
||||
h) show_help ;;
|
||||
*) log_error "Неверный аргумент"; exit 1 ;;
|
||||
esac
|
||||
@@ -406,6 +420,11 @@ main() {
|
||||
|
||||
log_info "Начало установки RFB Stat..."
|
||||
log_info "Команда: $team, Релиз: $release"
|
||||
if [ -n "$league" ]; then
|
||||
log_info "Лига: $league"
|
||||
else
|
||||
log_info "Лига: не указана (будет использовано значение по умолчанию)"
|
||||
fi
|
||||
|
||||
# Проверка прав root
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
@@ -439,7 +458,7 @@ main() {
|
||||
check_port
|
||||
|
||||
# Создание systemd сервисов
|
||||
create_systemd_services "$final_team"
|
||||
create_systemd_services "$final_team" "$league"
|
||||
|
||||
log_info "Настройка завершена!"
|
||||
|
||||
@@ -456,6 +475,9 @@ main() {
|
||||
log_info "Установка завершена успешно!"
|
||||
log_info "Приложение должно быть доступно по адресу: http://${ip_address}:8501"
|
||||
log_info "Команда: $final_team"
|
||||
if [ -n "$league" ]; then
|
||||
log_info "Лига: $league"
|
||||
fi
|
||||
log_info "Режим: $release"
|
||||
log_info ""
|
||||
log_info "Для просмотра логов:"
|
||||
2180
get_data.py
Normal file
2180
get_data.py
Normal file
File diff suppressed because it is too large
Load Diff
3165
get_data_old.py
Normal file
3165
get_data_old.py
Normal file
File diff suppressed because it is too large
Load Diff
2113
get_data_old2.py
Normal file
2113
get_data_old2.py
Normal file
File diff suppressed because it is too large
Load Diff
13
match_id.json
Normal file
13
match_id.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"10.0.85.2111": {"host": "", "tag": "vtb", "root": 1, "team": ""},
|
||||
"10.10.35.21": {"host": "gfx", "tag": "vtb", "root": 1, "team": "Lokomotiv Kuban"},
|
||||
"10.10.35.22": {"host": "krd", "tag": "vtb", "root": 1, "team": "Lokomotiv Kuban1"},
|
||||
"10.10.35.23": {"host": "ekb", "tag": "vtb", "root": 1, "team": "uralmash1"},
|
||||
"10.10.35.24": {"host": "per", "tag": "vtb", "root": 1, "team": "betcity parma1"},
|
||||
"10.10.35.25": {"host": "sar", "tag": "vtb", "root": 1, "team": "avtodor1"},
|
||||
"10.10.35.26": {"host": "spb", "tag": "vtb", "root": 1, "team": "zenit1"},
|
||||
"10.10.35.27": {"host": "sam", "tag": "vtb", "root": 1, "team": "samara1"},
|
||||
"10.10.35.28": {"host": "msk1", "tag": "vtb", "root": 1, "team": "mba-mai1"},
|
||||
"10.10.35.29": {"host": "msk2", "tag": "vtb", "root": 1, "team": "Pari Nizhny Novgorod1"},
|
||||
"10.10.35.30": {"host": "kaz", "tag": "vtb", "root": 1, "team": "unics1"}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
Dim url As String = "https://ekb.tvstart.ru/app/static/ekb_team1.json"
|
||||
Dim json As String = ""
|
||||
|
||||
' Чтение URL
|
||||
Try
|
||||
Dim wc As New System.Net.WebClient
|
||||
wc.Encoding = System.Text.Encoding.UTF8
|
||||
json = wc.DownloadString(url)
|
||||
Catch ex As Exception
|
||||
Console.WriteLine("Ошибка загрузки JSON: " & ex.Message)
|
||||
Return
|
||||
End Try
|
||||
|
||||
' --- Парсинг первых 12 num + NameGFX ---
|
||||
Dim nums(11) As String
|
||||
Dim names(11) As String
|
||||
Dim count As Integer = 0
|
||||
Dim pos As Integer = 0
|
||||
|
||||
While count < 12
|
||||
' ищем "num"
|
||||
Dim k As Integer = json.IndexOf("""num""", pos)
|
||||
If k = -1 Then Exit While
|
||||
|
||||
Dim c As Integer = json.IndexOf(":", k)
|
||||
If c = -1 Then Exit While
|
||||
|
||||
Dim j As Integer = c + 1
|
||||
While j < json.Length AndAlso Char.IsWhiteSpace(json(j))
|
||||
j += 1
|
||||
End While
|
||||
|
||||
Dim numVal As String = ""
|
||||
If j < json.Length AndAlso json(j) = """"c Then
|
||||
j += 1
|
||||
Dim startQ As Integer = j
|
||||
While j < json.Length AndAlso json(j) <> """"c
|
||||
j += 1
|
||||
End While
|
||||
numVal = json.Substring(startQ, j - startQ)
|
||||
j += 1
|
||||
Else
|
||||
Dim startN As Integer = j
|
||||
While j < json.Length AndAlso (Char.IsDigit(json(j)) OrElse json(j) = "-"c OrElse json(j) = "."c)
|
||||
j += 1
|
||||
End While
|
||||
numVal = json.Substring(startN, j - startN)
|
||||
End If
|
||||
|
||||
' ищем "NameGFX"
|
||||
pos = j
|
||||
Dim kn As Integer = json.IndexOf("""NameGFX""", pos)
|
||||
If kn = -1 Then Exit While
|
||||
|
||||
Dim cn As Integer = json.IndexOf(":", kn)
|
||||
If cn = -1 Then Exit While
|
||||
|
||||
Dim jn As Integer = cn + 1
|
||||
While jn < json.Length AndAlso Char.IsWhiteSpace(json(jn))
|
||||
jn += 1
|
||||
End While
|
||||
|
||||
Dim nameVal As String = ""
|
||||
If jn < json.Length AndAlso json(jn) = """"c Then
|
||||
jn += 1
|
||||
Dim startGN As Integer = jn
|
||||
While jn < json.Length AndAlso json(jn) <> """"c
|
||||
jn += 1
|
||||
End While
|
||||
nameVal = json.Substring(startGN, jn - startGN)
|
||||
jn += 1
|
||||
End If
|
||||
|
||||
nums(count) = numVal
|
||||
names(count) = nameVal
|
||||
count += 1
|
||||
|
||||
pos = jn
|
||||
End While
|
||||
|
||||
' --- Выводим результат ---
|
||||
Console.WriteLine("=== Первые " & count.ToString() & " игроков ===")
|
||||
For i As Integer = 0 To count - 1
|
||||
Console.WriteLine(nums(i) & "_" & names(i))
|
||||
Next
|
||||
|
||||
' --- Выводим результат ---
|
||||
Console.WriteLine("=== Первые " & count.ToString() & " игроков ===")
|
||||
For i As Integer = 0 To count - 1
|
||||
Console.WriteLine(nums(i) & "_" & names(i))
|
||||
|
||||
' === Отправляем в титр TeamRoster.gtzip ===
|
||||
' Номер
|
||||
API.Function("SetText", Input:="TeamRoster.gtzip", SelectedName:="PlayerNamber" & (i + 1).ToString() & ".Text", Value:=nums(i))
|
||||
' Имя
|
||||
API.Function("SetText", Input:="TeamRoster.gtzip", SelectedName:="PlayerName" & (i + 1).ToString() & ".Text", Value:=names(i))
|
||||
Next
|
||||
16
stats.py
16
stats.py
@@ -1,16 +0,0 @@
|
||||
import requests
|
||||
import json
|
||||
|
||||
url = "https://org.infobasket.su/Widget/GetOnline/921412?format=json&lang=ru"
|
||||
|
||||
response = requests.get(url)
|
||||
response.raise_for_status()
|
||||
data = response.json()
|
||||
|
||||
# print(data)
|
||||
|
||||
team1 = data["GameTeams"][0]
|
||||
team2 = data["GameTeams"][1]
|
||||
|
||||
print(team1)
|
||||
|
||||
@@ -1,421 +0,0 @@
|
||||
*** visual.py 2025-01-22 00:00:00.000000000 +0000
|
||||
--- visual.py 2025-10-07 00:00:00.000000000 +0000
|
||||
***************
|
||||
*** 1,10 ****
|
||||
import os
|
||||
import json
|
||||
import socket
|
||||
import platform
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import streamlit as st
|
||||
import sys
|
||||
from streamlit_autorefresh import st_autorefresh
|
||||
-
|
||||
st.set_page_config(
|
||||
page_title="Баскетбол",
|
||||
page_icon="🏀",
|
||||
layout="wide",
|
||||
--- 1,10 ----
|
||||
import os
|
||||
import json
|
||||
import socket
|
||||
import platform
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import streamlit as st
|
||||
import sys
|
||||
from streamlit_autorefresh import st_autorefresh
|
||||
+
|
||||
st.set_page_config(
|
||||
page_title="Баскетбол",
|
||||
page_icon="🏀",
|
||||
layout="wide",
|
||||
***************
|
||||
*** 164,169 ****
|
||||
--- 164,216 ----
|
||||
def ensure_state(key: str, default=None):
|
||||
# Инициализирует ключ один раз и возвращает значение
|
||||
return st.session_state.setdefault(key, default)
|
||||
|
||||
+
|
||||
+ # ======== UNIVERSAL SAFE RENDER WRAPPER ========
|
||||
+ def _is_empty_like(x) -> bool:
|
||||
+ if x is None:
|
||||
+ return True
|
||||
+ # DataFrame
|
||||
+ if isinstance(x, pd.DataFrame):
|
||||
+ return x.empty
|
||||
+ # Pandas Styler
|
||||
+ try:
|
||||
+ # импортируем лениво, чтобы не ломаться, если нет pandas.io.formats.style в рантайме
|
||||
+ from pandas.io.formats.style import Styler # type: ignore
|
||||
+ if isinstance(x, Styler):
|
||||
+ # у Styler нет __len__, но есть .data
|
||||
+ return getattr(x, "data", pd.DataFrame()).empty
|
||||
+ except Exception:
|
||||
+ pass
|
||||
+ # пустые коллекции
|
||||
+ if isinstance(x, (list, tuple, dict, set)):
|
||||
+ return len(x) == 0
|
||||
+ return False
|
||||
+
|
||||
+ def safe_show(func, *args, **kwargs):
|
||||
+ """
|
||||
+ Безопасно вызывает функции отображения Streamlit (dataframe, table, metric, image, markdown, ...).
|
||||
+ - Ничего не рендерит, если основной аргумент данных пуст/None.
|
||||
+ - Нормализует height (убирает None, <0; приводит к int).
|
||||
+ - Возвращает результат вызова func (нужно для dataframe-selection).
|
||||
+ - Перехватывает исключения и показывает предупреждение.
|
||||
+ """
|
||||
+ # Если среди позиционных/именованных аргументов есть пустые/None-данные — не показываем
|
||||
+ for a in args:
|
||||
+ if _is_empty_like(a):
|
||||
+ return None
|
||||
+ for k, v in kwargs.items():
|
||||
+ # пропускаем не-данные параметры (типа width/unsafe_allow_html)
|
||||
+ if k.lower() in ("height", "width", "use_container_width", "unsafe_allow_html", "on_select", "selection_mode", "column_config", "hide_index", "border", "delta_color", "key"):
|
||||
+ continue
|
||||
+ if _is_empty_like(v):
|
||||
+ return None
|
||||
+
|
||||
+ # height -> валидный int, иначе уберём
|
||||
+ if "height" in kwargs:
|
||||
+ h = kwargs.get("height")
|
||||
+ if h is None:
|
||||
+ kwargs.pop("height")
|
||||
+ else:
|
||||
+ try:
|
||||
+ h = int(h)
|
||||
+ if h < 0:
|
||||
+ kwargs.pop("height")
|
||||
+ else:
|
||||
+ kwargs["height"] = h
|
||||
+ except Exception:
|
||||
+ kwargs.pop("height")
|
||||
+
|
||||
+ try:
|
||||
+ return func(*args, **kwargs)
|
||||
+ except Exception as e:
|
||||
+ st.warning(f"⚠️ Ошибка при отображении: {e}")
|
||||
+ return None
|
||||
+ # ======== /SAFE WRAPPER ========
|
||||
+
|
||||
***************
|
||||
*** 221,231 ****
|
||||
col1, col4, col2, col5, col3 = st.columns([1, 5, 3, 5, 1])
|
||||
|
||||
t1 = (result.get("team1") or {})
|
||||
t2 = (result.get("team2") or {})
|
||||
- if t1.get("logo"):
|
||||
- col1.image(t1["logo"], width=100)
|
||||
team1_name = t1.get("name") or ""
|
||||
team2_name = t2.get("name") or ""
|
||||
- if team1_name or team2_name:
|
||||
- col2.markdown(
|
||||
- f"<h2 style='text-align: center'>{team1_name} — {team2_name}</h2>",
|
||||
- unsafe_allow_html=True,
|
||||
- )
|
||||
- if t2.get("logo"):
|
||||
- col3.image(t2["logo"], width=100)
|
||||
+ safe_show(col1.image, t1.get("logo"), width=100)
|
||||
+ if team1_name or team2_name:
|
||||
+ safe_show(
|
||||
+ col2.markdown,
|
||||
+ f"<h2 style='text-align: center'>{team1_name} — {team2_name}</h2>",
|
||||
+ unsafe_allow_html=True,
|
||||
+ )
|
||||
+ safe_show(col3.image, t2.get("logo"), width=100)
|
||||
|
||||
col4_1, col4_2, col4_3 = col4.columns((1, 1, 1))
|
||||
col5_1, col5_2, col5_3 = col5.columns((1, 1, 1))
|
||||
|
||||
***************
|
||||
*** 237,253 ****
|
||||
if isinstance(cached_team_stats, list) and len(cached_team_stats) > 0:
|
||||
v1 = cached_team_stats[0].get("val1")
|
||||
v2 = cached_team_stats[0].get("val2")
|
||||
if v1 is not None and v2 is not None:
|
||||
val1, val2 = int(v1), int(v2)
|
||||
delta_color_1 = "off" if val1 == val2 else "normal"
|
||||
- col4_1.metric("Points", v1, val1 - val2, delta_color_1)
|
||||
- col5_3.metric("Points", v2, val2 - val1, delta_color_1)
|
||||
+ safe_show(col4_1.metric, "Points", v1, val1 - val2, delta_color_1)
|
||||
+ safe_show(col5_3.metric, "Points", v2, val2 - val1, delta_color_1)
|
||||
|
||||
- col4_3.metric("TimeOuts", len(timeout1))
|
||||
- col5_1.metric("TimeOuts", len(timeout2))
|
||||
+ safe_show(col4_3.metric, "TimeOuts", len(timeout1))
|
||||
+ safe_show(col5_1.metric, "TimeOuts", len(timeout2))
|
||||
|
||||
if isinstance(cached_live_status, list) and cached_live_status:
|
||||
foulsA = (cached_live_status[0] or {}).get("foulsA")
|
||||
foulsB = (cached_live_status[0] or {}).get("foulsB")
|
||||
if foulsA is not None:
|
||||
- col4_2.metric("Fouls", foulsA)
|
||||
+ safe_show(col4_2.metric, "Fouls", foulsA)
|
||||
if foulsB is not None:
|
||||
- col5_2.metric("Fouls", foulsB)
|
||||
+ safe_show(col5_2.metric, "Fouls", foulsB)
|
||||
|
||||
***************
|
||||
*** 270,280 ****
|
||||
for q1, q2, col1_i, col2_i in zip(score_by_quarter_1, score_by_quarter_2, col_1_col, col_2_col):
|
||||
count_q += 1
|
||||
name_q = f"OT{count_q-4}" if count_q > 4 else f"Q{count_q}"
|
||||
try:
|
||||
delta_color = "off" if int(q1) == int(q2) else "normal"
|
||||
- col1_i.metric(name_q, q1, int(q1) - int(q2), delta_color, border=True)
|
||||
- col2_i.metric(name_q, q2, int(q2) - int(q1), delta_color, border=True)
|
||||
+ safe_show(col1_i.metric, name_q, q1, int(q1) - int(q2), delta_color, border=True)
|
||||
+ safe_show(col2_i.metric, name_q, q2, int(q2) - int(q1), delta_color, border=True)
|
||||
except (ValueError, TypeError):
|
||||
# если кривые данные в JSON, просто пропустим
|
||||
pass
|
||||
|
||||
***************
|
||||
*** 403,424 ****
|
||||
team1_styled = (
|
||||
team1_data.style.apply(highlight_grey, axis=1)
|
||||
.apply(highlight_foul, subset="foul")
|
||||
.apply(highlight_max, subset="pts")
|
||||
)
|
||||
team2_styled = (
|
||||
team2_data.style.apply(highlight_grey, axis=1)
|
||||
.apply(highlight_foul, subset="foul")
|
||||
.apply(highlight_max, subset="pts")
|
||||
)
|
||||
|
||||
# Вывод данных
|
||||
col_player1, col_player2 = tab_temp_1.columns((5, 5))
|
||||
- event1 = col_player1.dataframe(
|
||||
- team1_styled,
|
||||
- column_config=config,
|
||||
- hide_index=True,
|
||||
- height=460,
|
||||
- on_select="rerun",
|
||||
- selection_mode=[
|
||||
- "single-row",
|
||||
- ],
|
||||
- )
|
||||
- event2 = col_player2.dataframe(
|
||||
- team2_styled,
|
||||
- column_config=config,
|
||||
- hide_index=True,
|
||||
- height=460,
|
||||
- on_select="rerun",
|
||||
- selection_mode=[
|
||||
- "single-row",
|
||||
- ],
|
||||
- )
|
||||
+ event1 = safe_show(
|
||||
+ col_player1.dataframe,
|
||||
+ team1_styled,
|
||||
+ column_config=config,
|
||||
+ hide_index=True,
|
||||
+ height=460,
|
||||
+ on_select="rerun",
|
||||
+ selection_mode=["single-row"],
|
||||
+ )
|
||||
+ event2 = safe_show(
|
||||
+ col_player2.dataframe,
|
||||
+ team2_styled,
|
||||
+ column_config=config,
|
||||
+ hide_index=True,
|
||||
+ height=460,
|
||||
+ on_select="rerun",
|
||||
+ selection_mode=["single-row"],
|
||||
+ )
|
||||
|
||||
if event1 and getattr(event1, "selection", None) and event1.selection.get("rows"):
|
||||
selected_index1 = event1.selection["rows"][0]
|
||||
st.session_state["player1"] = (
|
||||
selected_index1 # Сохранение состояния в session_state
|
||||
***************
|
||||
*** 433,441 ****
|
||||
if player_data_1["num"]:
|
||||
z, a, b, c, d, e = col_player1.columns((1, 6, 1, 1, 1, 1))
|
||||
- z.metric("Номер", player_data_1["num"], border=False)
|
||||
- a.metric("Игрок", player_data_1["NameGFX"], border=False)
|
||||
- b.metric("Амплуа", player_data_1["roleShort"], border=False)
|
||||
- c.metric("Возраст", player_data_1["age"], border=False)
|
||||
- d.metric("Рост", player_data_1["height"].split()[0], border=False)
|
||||
- e.metric("Вес", player_data_1["weight"].split()[0], border=False)
|
||||
-
|
||||
- col_player1.dataframe(
|
||||
- selected_player_1,
|
||||
- column_config=config_season,
|
||||
- hide_index=True,
|
||||
- )
|
||||
+ safe_show(z.metric, "Номер", player_data_1["num"], border=False)
|
||||
+ safe_show(a.metric, "Игрок", player_data_1["NameGFX"], border=False)
|
||||
+ safe_show(b.metric, "Амплуа", player_data_1["roleShort"], border=False)
|
||||
+ safe_show(c.metric, "Возраст", player_data_1["age"], border=False)
|
||||
+ safe_show(d.metric, "Рост", player_data_1["height"].split()[0], border=False)
|
||||
+ safe_show(e.metric, "Вес", player_data_1["weight"].split()[0], border=False)
|
||||
+
|
||||
+ safe_show(
|
||||
+ col_player1.dataframe,
|
||||
+ selected_player_1,
|
||||
+ column_config=config_season,
|
||||
+ hide_index=True,
|
||||
+ )
|
||||
***************
|
||||
*** 446,454 ****
|
||||
if player_data_2["num"]:
|
||||
z, a, b, c, d, e = col_player2.columns((1, 6, 1, 1, 1, 1))
|
||||
- z.metric("Номер", player_data_2["num"], border=False)
|
||||
- a.metric("Игрок", player_data_2["NameGFX"], border=False)
|
||||
- b.metric("Амплуа", player_data_2["roleShort"], border=False)
|
||||
- c.metric("Возраст", player_data_2["age"], border=False)
|
||||
- d.metric("Рост", player_data_2["height"].split()[0], border=False)
|
||||
- e.metric("Вес", player_data_2["weight"].split()[0], border=False)
|
||||
-
|
||||
- col_player2.dataframe(
|
||||
- selected_player_2,
|
||||
- column_config=config_season,
|
||||
- hide_index=True,
|
||||
- )
|
||||
+ safe_show(z.metric, "Номер", player_data_2["num"], border=False)
|
||||
+ safe_show(a.metric, "Игрок", player_data_2["NameGFX"], border=False)
|
||||
+ safe_show(b.metric, "Амплуа", player_data_2["roleShort"], border=False)
|
||||
+ safe_show(c.metric, "Возраст", player_data_2["age"], border=False)
|
||||
+ safe_show(d.metric, "Рост", player_data_2["height"].split()[0], border=False)
|
||||
+ safe_show(e.metric, "Вес", player_data_2["weight"].split()[0], border=False)
|
||||
+
|
||||
+ safe_show(
|
||||
+ col_player2.dataframe,
|
||||
+ selected_player_2,
|
||||
+ column_config=config_season,
|
||||
+ hide_index=True,
|
||||
+ )
|
||||
***************
|
||||
*** 459,468 ****
|
||||
if isinstance(cached_team_stats, list) and len(cached_team_stats) >= 34:
|
||||
cached_team_stats_new = [
|
||||
cached_team_stats[0],
|
||||
*cached_team_stats[25:29],
|
||||
cached_team_stats[7],
|
||||
cached_team_stats[33],
|
||||
*cached_team_stats[9:11],
|
||||
*cached_team_stats[15:17],
|
||||
]
|
||||
- tab_temp_2.table(cached_team_stats_new)
|
||||
+ safe_show(tab_temp_2.table, cached_team_stats_new)
|
||||
|
||||
***************
|
||||
*** 470,484 ****
|
||||
- if isinstance(cached_referee, (list, pd.DataFrame)):
|
||||
- tab_temp_3.dataframe(cached_referee, height=600, column_config={"flag": st.column_config.ImageColumn("flag")})
|
||||
-
|
||||
-
|
||||
column_config_ref = {
|
||||
"flag": st.column_config.ImageColumn(
|
||||
"flag",
|
||||
),
|
||||
}
|
||||
|
||||
if cached_referee:
|
||||
- tab_temp_3.dataframe(cached_referee, height=600, column_config=column_config_ref)
|
||||
+ safe_show(tab_temp_3.dataframe, cached_referee, height=600, column_config=column_config_ref)
|
||||
|
||||
***************
|
||||
*** 503,511 ****
|
||||
styled = df_st.style.apply(highlight_teams, axis=1)
|
||||
- tab_temp_4.dataframe(
|
||||
- styled,
|
||||
- column_config={"logo": st.column_config.ImageColumn("logo")},
|
||||
- hide_index=True,
|
||||
- height=610,
|
||||
- )
|
||||
+ safe_show(
|
||||
+ tab_temp_4.dataframe,
|
||||
+ styled,
|
||||
+ column_config={"logo": st.column_config.ImageColumn("logo")},
|
||||
+ hide_index=True,
|
||||
+ height=610,
|
||||
+ )
|
||||
|
||||
***************
|
||||
*** 552,558 ****
|
||||
]
|
||||
col.write(q)
|
||||
- col.dataframe(df_col)
|
||||
+ safe_show(col.dataframe, df_col)
|
||||
|
||||
# Овертаймы
|
||||
for index, col in enumerate(columns_quarters):
|
||||
q = columns_quarters_name_ot[index]
|
||||
df_col = [
|
||||
--- 564,570 ----
|
||||
***************
|
||||
*** 572,578 ****
|
||||
]
|
||||
col.write(q)
|
||||
- col.dataframe(df_col)
|
||||
+ safe_show(col.dataframe, df_col)
|
||||
|
||||
***************
|
||||
*** 582,586 ****
|
||||
if isinstance(cached_play_by_play, list) and isinstance(cached_game_online, dict):
|
||||
plays = (cached_game_online.get("result") or {}).get("plays") or []
|
||||
if plays:
|
||||
- tab_temp_6.table(cached_play_by_play)
|
||||
+ safe_show(tab_temp_6.table, cached_play_by_play)
|
||||
|
||||
***************
|
||||
*** 703,724 ****
|
||||
height1 = 38 * max(count_game_1, 10)
|
||||
height2 = 38 * max(count_game_2, 10)
|
||||
- col1_schedule.dataframe(
|
||||
- team1_data,
|
||||
- hide_index=True,
|
||||
- height=int(min(height1, 1200)),
|
||||
- column_config=column_config,
|
||||
- )
|
||||
- col2_schedule.dataframe(
|
||||
- team2_data,
|
||||
- hide_index=True,
|
||||
- height=int(min(height2, 1200)),
|
||||
- column_config=column_config,
|
||||
- )
|
||||
+ safe_show(
|
||||
+ col1_schedule.dataframe,
|
||||
+ team1_data,
|
||||
+ hide_index=True,
|
||||
+ height=int(min(height1, 1200)),
|
||||
+ column_config=column_config,
|
||||
+ )
|
||||
+ safe_show(
|
||||
+ col2_schedule.dataframe,
|
||||
+ team2_data,
|
||||
+ hide_index=True,
|
||||
+ height=int(min(height2, 1200)),
|
||||
+ column_config=column_config,
|
||||
+ )
|
||||
|
||||
***************
|
||||
*** 836,845 ****
|
||||
filtered_data_pbp = temp_data_pbp[mask1]
|
||||
count_pbp = len(filtered_data_pbp)
|
||||
|
||||
column_pbp = ["num", "info", "who", "period", "time"]
|
||||
column_config_pbp = {
|
||||
"info": st.column_config.TextColumn(width="medium"),
|
||||
"who": st.column_config.TextColumn(width="large"),
|
||||
}
|
||||
- col2_pbp.dataframe(
|
||||
- filtered_data_pbp[column_pbp],
|
||||
- column_config=column_config_pbp,
|
||||
- hide_index=True,
|
||||
- height=(38 * count_pbp if count_pbp > 10 else None),
|
||||
- )
|
||||
+ safe_show(
|
||||
+ col2_pbp.dataframe,
|
||||
+ filtered_data_pbp[column_pbp],
|
||||
+ column_config=column_config_pbp,
|
||||
+ hide_index=True,
|
||||
+ height=(38 * count_pbp if count_pbp > 10 else None),
|
||||
+ )
|
||||
else:
|
||||
st.info("Данных play-by-play нет.")
|
||||
Reference in New Issue
Block a user