ch
This commit is contained in:
117
.gitignore
vendored
117
.gitignore
vendored
@@ -1,4 +1,6 @@
|
||||
data/
|
||||
data
|
||||
.streamlit
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
@@ -20,9 +22,19 @@ lib64/
|
||||
parts/
|
||||
sdist/
|
||||
var/
|
||||
wheels/
|
||||
pip-wheel-metadata/
|
||||
share/python-wheels/
|
||||
*.egg-info/
|
||||
.installed.cfg
|
||||
*.egg
|
||||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script from a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
@@ -38,15 +50,79 @@ htmlcov/
|
||||
nosetests.xml
|
||||
coverage.xml
|
||||
*.cover
|
||||
*.py,cover
|
||||
.hypothesis/
|
||||
.pytest_cache/
|
||||
|
||||
# Translations
|
||||
*.mo
|
||||
*.pot
|
||||
|
||||
# Django stuff:
|
||||
*.log
|
||||
local_settings.py
|
||||
db.sqlite3
|
||||
db.sqlite3-journal
|
||||
|
||||
# Flask stuff:
|
||||
instance/
|
||||
.webassets-cache
|
||||
|
||||
# Scrapy stuff:
|
||||
.scrapy
|
||||
|
||||
# Sphinx documentation
|
||||
docs/_build/
|
||||
|
||||
# PyBuilder
|
||||
target/
|
||||
|
||||
# Jupyter Notebook
|
||||
.ipynb_checkpoints
|
||||
|
||||
# IPython
|
||||
profile_default/
|
||||
ipython_config.py
|
||||
|
||||
# pyenv
|
||||
.python-version
|
||||
|
||||
# pipenv
|
||||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||
# install all needed dependencies.
|
||||
#Pipfile.lock
|
||||
|
||||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||
__pypackages__/
|
||||
|
||||
# Celery stuff
|
||||
celerybeat-schedule
|
||||
celerybeat.pid
|
||||
|
||||
# SageMath parsed files
|
||||
*.sage.py
|
||||
|
||||
# Environments
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
.spyproject
|
||||
|
||||
# Rope project settings
|
||||
.ropeproject
|
||||
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
.dmypy.json
|
||||
@@ -55,36 +131,23 @@ dmypy.json
|
||||
# Pyre type checker
|
||||
.pyre/
|
||||
|
||||
# VS Code
|
||||
# IDE
|
||||
.vscode/
|
||||
|
||||
# PyCharm
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Local envs
|
||||
.env
|
||||
.venv
|
||||
env/
|
||||
venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
|
||||
# MacOS
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
# Windows
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
|
||||
# MinIO test data
|
||||
# Project specific
|
||||
data/
|
||||
*.zip
|
||||
*.xlsx
|
||||
*.xls
|
||||
*.xlsm
|
||||
|
||||
# MinIO data directory
|
||||
minio_data/
|
||||
minio_test/
|
||||
minio/
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
|
||||
# Streamlit cache
|
||||
.streamlit/
|
||||
|
||||
182
README.md
Normal file
182
README.md
Normal file
@@ -0,0 +1,182 @@
|
||||
# 🚀 NIN Excel Parsers API - Полная система
|
||||
|
||||
Полноценная система для парсинга Excel отчетов нефтеперерабатывающих заводов (НПЗ) с использованием FastAPI, MinIO и Streamlit.
|
||||
|
||||
## 🏗️ Архитектура проекта
|
||||
|
||||
Проект состоит из **двух изолированных пакетов**:
|
||||
|
||||
- **`python_parser/`** - FastAPI сервер + парсеры Excel
|
||||
- **`streamlit_app/`** - Веб-интерфейс для демонстрации API
|
||||
|
||||
## 🚀 Быстрый запуск
|
||||
|
||||
### **Вариант 1: Все сервисы в Docker (рекомендуется)**
|
||||
```bash
|
||||
# Запуск всех сервисов: MinIO + FastAPI + Streamlit
|
||||
docker-compose up -d
|
||||
|
||||
# Доступ:
|
||||
# - MinIO Console: http://localhost:9001
|
||||
# - FastAPI: http://localhost:8000
|
||||
# - Streamlit: http://localhost:8501
|
||||
# - API Docs: http://localhost:8000/docs
|
||||
```
|
||||
|
||||
### **Вариант 2: Только MinIO в Docker + сервисы локально**
|
||||
```bash
|
||||
# Запуск MinIO в Docker
|
||||
docker-compose up -d minio
|
||||
|
||||
# Запуск FastAPI локально
|
||||
cd python_parser
|
||||
python run_dev.py
|
||||
|
||||
# В отдельном терминале - Streamlit
|
||||
cd streamlit_app
|
||||
streamlit run app.py
|
||||
```
|
||||
|
||||
### **Вариант 3: Только MinIO в Docker**
|
||||
```bash
|
||||
# Запуск только MinIO
|
||||
docker-compose up -d minio
|
||||
```
|
||||
|
||||
## 📋 Описание сервисов
|
||||
|
||||
- **MinIO** (порт 9000-9001): S3-совместимое хранилище для данных
|
||||
- **FastAPI** (порт 8000): API сервер для парсинга Excel файлов
|
||||
- **Streamlit** (порт 8501): Веб-интерфейс для демонстрации API
|
||||
|
||||
## 📁 Структура проекта
|
||||
|
||||
```
|
||||
python_parser_cf/ # Корень проекта
|
||||
├── python_parser/ # Пакет FastAPI + парсеры
|
||||
│ ├── app/ # FastAPI приложение
|
||||
│ │ ├── main.py # Основной файл приложения
|
||||
│ │ └── schemas/ # Pydantic схемы
|
||||
│ ├── core/ # Бизнес-логика
|
||||
│ │ ├── models.py # Модели данных
|
||||
│ │ ├── ports.py # Интерфейсы (порты)
|
||||
│ │ └── services.py # Сервисы
|
||||
│ ├── adapters/ # Адаптеры для внешних систем
|
||||
│ │ ├── storage.py # MinIO адаптер
|
||||
│ │ └── parsers/ # Парсеры Excel файлов
|
||||
│ ├── data/ # Тестовые данные
|
||||
│ ├── Dockerfile # Docker образ для FastAPI
|
||||
│ ├── requirements.txt # Зависимости FastAPI
|
||||
│ └── run_dev.py # Запуск FastAPI локально
|
||||
├── streamlit_app/ # Пакет Streamlit
|
||||
│ ├── app.py # Основное Streamlit приложение
|
||||
│ ├── requirements.txt # Зависимости Streamlit
|
||||
│ ├── Dockerfile # Docker образ для Streamlit
|
||||
│ ├── .streamlit/ # Конфигурация Streamlit
|
||||
│ │ └── config.toml # Настройки
|
||||
│ └── README.md # Документация Streamlit
|
||||
├── docker-compose.yml # Docker Compose конфигурация
|
||||
├── .gitignore # Git исключения
|
||||
└── README.md # Общая документация
|
||||
```
|
||||
|
||||
## 🔍 Доступные эндпоинты
|
||||
|
||||
- **GET /** - Информация об API
|
||||
- **GET /docs** - Swagger документация
|
||||
- **GET /parsers** - Список доступных парсеров
|
||||
- **GET /parsers/{parser_name}/getters** - Информация о геттерах парсера
|
||||
- **POST /svodka_pm/upload-zip** - Загрузка сводок ПМ
|
||||
- **POST /svodka_ca/upload** - Загрузка сводок ЦА
|
||||
- **POST /monitoring_fuel/upload-zip** - Загрузка мониторинга топлива
|
||||
- **POST /svodka_pm/get_data** - Получение данных сводок ПМ
|
||||
- **POST /svodka_ca/get_data** - Получение данных сводок ЦА
|
||||
- **POST /monitoring_fuel/get_data** - Получение данных мониторинга топлива
|
||||
|
||||
## 📊 Поддерживаемые типы отчетов
|
||||
|
||||
1. **svodka_pm** - Сводки по переработке нефти (ПМ)
|
||||
- Геттеры: `single_og`, `total_ogs`
|
||||
2. **svodka_ca** - Сводки по переработке нефти (ЦА)
|
||||
- Геттеры: `get_data`
|
||||
3. **monitoring_fuel** - Мониторинг топлива
|
||||
- Геттеры: `total_by_columns`, `month_by_code`
|
||||
|
||||
## 🏗️ Архитектура
|
||||
|
||||
Проект использует **Hexagonal Architecture (Ports and Adapters)**:
|
||||
|
||||
- **Порты (Ports)**: Интерфейсы для бизнес-логики
|
||||
- **Адаптеры (Adapters)**: Реализации для внешних систем
|
||||
- **Сервисы (Services)**: Бизнес-логика приложения
|
||||
|
||||
### Система геттеров парсеров
|
||||
|
||||
Каждый парсер может иметь несколько методов получения данных (геттеров):
|
||||
- Регистрация геттеров в словаре с метаданными
|
||||
- Валидация параметров для каждого геттера
|
||||
- Единый интерфейс `get_value(getter_name, params)`
|
||||
|
||||
## 🐳 Docker
|
||||
|
||||
### Сборка образов:
|
||||
```bash
|
||||
# FastAPI
|
||||
docker build -t nin-fastapi ./python_parser
|
||||
|
||||
# Streamlit
|
||||
docker build -t nin-streamlit ./streamlit_app
|
||||
```
|
||||
|
||||
### Запуск отдельных сервисов:
|
||||
```bash
|
||||
# Только MinIO
|
||||
docker-compose up -d minio
|
||||
|
||||
# MinIO + FastAPI
|
||||
docker-compose up -d minio fastapi
|
||||
|
||||
# Все сервисы
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## 🛑 Остановка
|
||||
|
||||
### Остановка Docker сервисов:
|
||||
```bash
|
||||
# Все сервисы
|
||||
docker-compose down
|
||||
|
||||
# Только MinIO
|
||||
docker-compose stop minio
|
||||
```
|
||||
|
||||
### Остановка локальных сервисов:
|
||||
```bash
|
||||
# Нажмите Ctrl+C в терминале с FastAPI/Streamlit
|
||||
```
|
||||
|
||||
## 🔧 Разработка
|
||||
|
||||
### Добавление нового парсера:
|
||||
|
||||
1. Создайте файл в `python_parser/adapters/parsers/`
|
||||
2. Реализуйте интерфейс `ParserPort`
|
||||
3. Добавьте в `python_parser/core/services.py`
|
||||
4. Создайте схемы в `python_parser/app/schemas/`
|
||||
5. Добавьте эндпоинты в `python_parser/app/main.py`
|
||||
|
||||
### Тестирование:
|
||||
|
||||
```bash
|
||||
# Запуск тестов
|
||||
cd python_parser
|
||||
pytest
|
||||
|
||||
# Запуск с покрытием
|
||||
pytest --cov=.
|
||||
```
|
||||
|
||||
## 📝 Лицензия
|
||||
|
||||
Проект разработан для внутреннего использования НИН.
|
||||
@@ -170,16 +170,11 @@ def main():
|
||||
|
||||
if not port_8000_ok:
|
||||
print("\n🔧 РЕШЕНИЕ: Запустите FastAPI сервер")
|
||||
print("python run_dev.py")
|
||||
print("docker-compose up -d fastapi")
|
||||
|
||||
if not port_8501_ok:
|
||||
print("\n🔧 РЕШЕНИЕ: Запустите Streamlit")
|
||||
print("python run_streamlit.py")
|
||||
|
||||
print("\n🚀 Для автоматического запуска используйте:")
|
||||
print("python start_demo.py")
|
||||
print("\n🔍 Для пошагового запуска используйте:")
|
||||
print("python run_manual.py")
|
||||
print("docker-compose up -d streamlit")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
34
create_test_excel.py
Normal file
34
create_test_excel.py
Normal file
@@ -0,0 +1,34 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Создание тестового Excel файла для тестирования API
|
||||
"""
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
def create_test_excel():
|
||||
"""Создание тестового Excel файла"""
|
||||
|
||||
# Создаем тестовые данные
|
||||
data = {
|
||||
'name': ['Установка 1', 'Установка 2', 'Установка 3'],
|
||||
'normativ': [100, 200, 300],
|
||||
'total': [95, 195, 295],
|
||||
'total_1': [90, 190, 290]
|
||||
}
|
||||
|
||||
df = pd.DataFrame(data)
|
||||
|
||||
# Сохраняем в Excel
|
||||
filename = 'test_file.xlsx'
|
||||
with pd.ExcelWriter(filename, engine='openpyxl') as writer:
|
||||
df.to_excel(writer, sheet_name='Мониторинг потребления', index=False)
|
||||
|
||||
print(f"✅ Тестовый файл создан: {filename}")
|
||||
print(f"📊 Содержимое: {len(df)} строк, {len(df.columns)} столбцов")
|
||||
print(f"📋 Столбцы: {list(df.columns)}")
|
||||
|
||||
return filename
|
||||
|
||||
if __name__ == "__main__":
|
||||
create_test_excel()
|
||||
@@ -10,11 +10,11 @@ services:
|
||||
MINIO_ROOT_PASSWORD: minioadmin
|
||||
command: server /data --console-address ":9001"
|
||||
volumes:
|
||||
- minio_data:/data
|
||||
- ./minio_data:/data
|
||||
restart: unless-stopped
|
||||
|
||||
fastapi:
|
||||
build: .
|
||||
build: ./python_parser
|
||||
container_name: svodka_fastapi
|
||||
ports:
|
||||
- "8000:8000"
|
||||
@@ -35,9 +35,7 @@ services:
|
||||
- "8501:8501"
|
||||
environment:
|
||||
- API_BASE_URL=http://fastapi:8000
|
||||
- DOCKER_ENV=true
|
||||
depends_on:
|
||||
- fastapi
|
||||
restart: unless-stopped
|
||||
|
||||
volumes:
|
||||
minio_data:
|
||||
restart: unless-stopped
|
||||
@@ -1,28 +0,0 @@
|
||||
[server]
|
||||
port = 8501
|
||||
address = "localhost"
|
||||
headless = false
|
||||
enableCORS = false
|
||||
enableXsrfProtection = false
|
||||
|
||||
[browser]
|
||||
gatherUsageStats = false
|
||||
serverAddress = "localhost"
|
||||
serverPort = 8501
|
||||
|
||||
[theme]
|
||||
primaryColor = "#FF4B4B"
|
||||
backgroundColor = "#FFFFFF"
|
||||
secondaryBackgroundColor = "#F0F2F6"
|
||||
textColor = "#262730"
|
||||
font = "sans serif"
|
||||
|
||||
[client]
|
||||
showErrorDetails = true
|
||||
caching = true
|
||||
displayEnabled = true
|
||||
|
||||
[runner]
|
||||
magicEnabled = true
|
||||
installTracer = false
|
||||
fixMatplotlib = true
|
||||
@@ -1,66 +0,0 @@
|
||||
# 🚀 Быстрый старт NIN Excel Parsers API
|
||||
|
||||
## 🐳 Запуск через Docker (рекомендуется)
|
||||
|
||||
### Вариант 1: MinIO + FastAPI в Docker
|
||||
```bash
|
||||
# Запуск всех сервисов
|
||||
docker-compose up -d --build
|
||||
|
||||
# Проверка
|
||||
curl http://localhost:8000
|
||||
curl http://localhost:9001
|
||||
```
|
||||
|
||||
### Вариант 2: Только MinIO в Docker
|
||||
```bash
|
||||
# Запуск только MinIO
|
||||
docker-compose up -d minio
|
||||
|
||||
# Проверка
|
||||
curl http://localhost:9001
|
||||
```
|
||||
|
||||
## 🖥️ Запуск FastAPI локально
|
||||
|
||||
```bash
|
||||
# Если MinIO в Docker
|
||||
python run_dev.py
|
||||
|
||||
# Проверка
|
||||
curl http://localhost:8000
|
||||
```
|
||||
|
||||
## 📊 Запуск Streamlit
|
||||
|
||||
```bash
|
||||
# В отдельном терминале
|
||||
python run_streamlit.py
|
||||
```
|
||||
|
||||
## 🌐 Доступные URL
|
||||
|
||||
- **FastAPI API**: http://localhost:8000
|
||||
- **API документация**: http://localhost:8000/docs
|
||||
- **MinIO консоль**: http://localhost:9001
|
||||
- **Streamlit интерфейс**: http://localhost:8501
|
||||
|
||||
## 🛑 Остановка
|
||||
|
||||
```bash
|
||||
# Остановка Docker
|
||||
docker-compose down
|
||||
|
||||
# Остановка Streamlit
|
||||
# Ctrl+C в терминале
|
||||
```
|
||||
|
||||
## 🔧 Диагностика
|
||||
|
||||
```bash
|
||||
# Проверка состояния
|
||||
python check_services.py
|
||||
|
||||
# Просмотр логов Docker
|
||||
docker-compose logs
|
||||
```
|
||||
@@ -1,63 +1,28 @@
|
||||
# NIN Excel Parsers API
|
||||
# 📊 Python Parser - FastAPI + Парсеры Excel
|
||||
|
||||
API для парсинга Excel отчетов нефтеперерабатывающих заводов (НПЗ) с использованием FastAPI и MinIO для хранения данных.
|
||||
Пакет FastAPI сервера и парсеров Excel для нефтеперерабатывающих заводов.
|
||||
|
||||
## 🚀 Быстрый запуск
|
||||
|
||||
### **Вариант 1: Все сервисы в Docker (рекомендуется)**
|
||||
### **Локально:**
|
||||
```bash
|
||||
# Запуск всех сервисов: MinIO + FastAPI + Streamlit
|
||||
docker-compose up -d
|
||||
# Установка зависимостей
|
||||
pip install -r requirements.txt
|
||||
|
||||
# Доступ:
|
||||
# - MinIO Console: http://localhost:9001
|
||||
# - FastAPI: http://localhost:8000
|
||||
# - Streamlit: http://localhost:8501
|
||||
# - API Docs: http://localhost:8000/docs
|
||||
```
|
||||
|
||||
### **Вариант 2: Только MinIO в Docker + FastAPI локально**
|
||||
```bash
|
||||
# Запуск MinIO в Docker
|
||||
docker-compose up -d minio
|
||||
|
||||
# Запуск FastAPI локально
|
||||
# Запуск FastAPI сервера
|
||||
python run_dev.py
|
||||
|
||||
# В отдельном терминале запуск Streamlit
|
||||
cd streamlit_app
|
||||
streamlit run app.py
|
||||
```
|
||||
|
||||
### **Вариант 3: Только MinIO в Docker**
|
||||
### **В Docker:**
|
||||
```bash
|
||||
# Запуск только MinIO
|
||||
docker-compose up -d minio
|
||||
# Сборка образа
|
||||
docker build -t nin-fastapi .
|
||||
|
||||
# Запуск контейнера
|
||||
docker run -p 8000:8000 nin-fastapi
|
||||
```
|
||||
|
||||
## 📋 Описание сервисов
|
||||
|
||||
- **MinIO** (порт 9000-9001): S3-совместимое хранилище для данных
|
||||
- **FastAPI** (порт 8000): API сервер для парсинга Excel файлов
|
||||
- **Streamlit** (порт 8501): Веб-интерфейс для демонстрации API
|
||||
|
||||
## 🛑 Остановка
|
||||
|
||||
### Остановка Docker сервисов:
|
||||
```bash
|
||||
# Все сервисы
|
||||
docker-compose down
|
||||
|
||||
# Только MinIO
|
||||
docker-compose stop minio
|
||||
```
|
||||
|
||||
### Остановка локальных сервисов:
|
||||
```bash
|
||||
# Нажмите Ctrl+C в терминале с FastAPI/Streamlit
|
||||
```
|
||||
|
||||
## 📁 Структура проекта
|
||||
## 📁 Структура пакета
|
||||
|
||||
```
|
||||
python_parser/
|
||||
@@ -71,18 +36,13 @@ python_parser/
|
||||
├── adapters/ # Адаптеры для внешних систем
|
||||
│ ├── storage.py # MinIO адаптер
|
||||
│ └── parsers/ # Парсеры Excel файлов
|
||||
├── streamlit_app/ # Изолированный Streamlit пакет
|
||||
│ ├── app.py # Основное Streamlit приложение
|
||||
│ ├── requirements.txt # Зависимости Streamlit
|
||||
│ ├── Dockerfile # Docker образ для Streamlit
|
||||
│ └── .streamlit/ # Конфигурация Streamlit
|
||||
├── data/ # Тестовые данные
|
||||
├── docker-compose.yml # Docker Compose конфигурация
|
||||
├── Dockerfile # Docker образ для FastAPI
|
||||
└── run_dev.py # Запуск FastAPI локально
|
||||
├── requirements.txt # Зависимости Python
|
||||
└── run_dev.py # Запуск FastAPI локально
|
||||
```
|
||||
|
||||
## 🔍 Доступные эндпоинты
|
||||
## 🔍 Основные эндпоинты
|
||||
|
||||
- **GET /** - Информация об API
|
||||
- **GET /docs** - Swagger документация
|
||||
@@ -95,7 +55,7 @@ python_parser/
|
||||
- **POST /svodka_ca/get_data** - Получение данных сводок ЦА
|
||||
- **POST /monitoring_fuel/get_data** - Получение данных мониторинга топлива
|
||||
|
||||
## 📊 Поддерживаемые типы отчетов
|
||||
## 📊 Поддерживаемые парсеры
|
||||
|
||||
1. **svodka_pm** - Сводки по переработке нефти (ПМ)
|
||||
- Геттеры: `single_og`, `total_ogs`
|
||||
@@ -106,7 +66,7 @@ python_parser/
|
||||
|
||||
## 🏗️ Архитектура
|
||||
|
||||
Проект использует **Hexagonal Architecture (Ports and Adapters)**:
|
||||
Использует **Hexagonal Architecture (Ports and Adapters)**:
|
||||
|
||||
- **Порты (Ports)**: Интерфейсы для бизнес-логики
|
||||
- **Адаптеры (Adapters)**: Реализации для внешних систем
|
||||
@@ -119,25 +79,26 @@ python_parser/
|
||||
- Валидация параметров для каждого геттера
|
||||
- Единый интерфейс `get_value(getter_name, params)`
|
||||
|
||||
## 🐳 Docker
|
||||
## 🔧 Разработка
|
||||
|
||||
### Добавление нового парсера:
|
||||
|
||||
1. Создайте файл в `adapters/parsers/`
|
||||
2. Реализуйте интерфейс `ParserPort`
|
||||
3. Добавьте в `core/services.py`
|
||||
4. Создайте схемы в `app/schemas/`
|
||||
5. Добавьте эндпоинты в `app/main.py`
|
||||
|
||||
### Тестирование:
|
||||
|
||||
### Сборка образов:
|
||||
```bash
|
||||
# FastAPI
|
||||
docker build -t nin-fastapi .
|
||||
# Запуск тестов
|
||||
pytest
|
||||
|
||||
# Streamlit
|
||||
docker build -t nin-streamlit ./streamlit_app
|
||||
# Запуск с покрытием
|
||||
pytest --cov=.
|
||||
```
|
||||
|
||||
### Запуск отдельных сервисов:
|
||||
```bash
|
||||
# Только MinIO
|
||||
docker-compose up -d minio
|
||||
## 📝 Примечание
|
||||
|
||||
# MinIO + FastAPI
|
||||
docker-compose up -d minio fastapi
|
||||
|
||||
# Все сервисы
|
||||
docker-compose up -d
|
||||
```
|
||||
Этот пакет является частью большей системы. Для полной документации и запуска всех сервисов см. README.md в корне проекта.
|
||||
@@ -1,186 +0,0 @@
|
||||
# 🚀 Streamlit Demo для NIN Excel Parsers API
|
||||
|
||||
## Описание
|
||||
|
||||
Streamlit приложение для демонстрации работы всех API эндпоинтов NIN Excel Parsers. Предоставляет удобный веб-интерфейс для тестирования функциональности парсеров.
|
||||
|
||||
## Возможности
|
||||
|
||||
- 📤 **Загрузка файлов**: Загрузка ZIP архивов и Excel файлов
|
||||
- 📊 **Сводки ПМ**: Работа с плановыми и фактическими данными
|
||||
- 🏭 **Сводки СА**: Парсинг сводок центрального аппарата
|
||||
- ⛽ **Мониторинг топлива**: Анализ данных по топливу
|
||||
- 📱 **Адаптивный интерфейс**: Удобное использование на всех устройствах
|
||||
|
||||
## Установка и запуск
|
||||
|
||||
### 1. Установка зависимостей
|
||||
|
||||
```bash
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### 2. Запуск FastAPI сервера
|
||||
|
||||
В одном терминале:
|
||||
```bash
|
||||
python run_dev.py
|
||||
```
|
||||
|
||||
### 3. Запуск Streamlit приложения
|
||||
|
||||
В другом терминале:
|
||||
```bash
|
||||
python run_streamlit.py
|
||||
```
|
||||
|
||||
Или напрямую:
|
||||
```bash
|
||||
streamlit run streamlit_app.py
|
||||
```
|
||||
|
||||
### 4. Открытие в браузере
|
||||
|
||||
Приложение автоматически откроется по адресу: http://localhost:8501
|
||||
|
||||
## Конфигурация
|
||||
|
||||
### Переменные окружения
|
||||
|
||||
```bash
|
||||
# URL API сервера
|
||||
export API_BASE_URL="http://localhost:8000"
|
||||
|
||||
# Порт Streamlit
|
||||
export STREAMLIT_PORT="8501"
|
||||
|
||||
# Хост Streamlit
|
||||
export STREAMLIT_HOST="localhost"
|
||||
```
|
||||
|
||||
### Настройки Streamlit
|
||||
|
||||
Файл `.streamlit/config.toml` содержит настройки:
|
||||
- Порт: 8501
|
||||
- Хост: localhost
|
||||
- Тема: Кастомная цветовая схема
|
||||
- Безопасность: Отключены CORS и XSRF для локальной разработки
|
||||
|
||||
## Структура приложения
|
||||
|
||||
### Вкладки
|
||||
|
||||
1. **📤 Загрузка файлов**
|
||||
- Загрузка сводок ПМ (ZIP)
|
||||
- Загрузка мониторинга топлива (ZIP)
|
||||
- Загрузка сводки СА (Excel)
|
||||
|
||||
2. **📊 Сводки ПМ**
|
||||
- Данные по одному ОГ
|
||||
- Данные по всем ОГ
|
||||
- Выбор кодов строк и столбцов
|
||||
|
||||
3. **🏭 Сводки СА**
|
||||
- Выбор режимов (план/факт/норматив)
|
||||
- Выбор таблиц для анализа
|
||||
|
||||
4. **⛽ Мониторинг топлива**
|
||||
- Агрегация по колонкам
|
||||
- Данные за конкретный месяц
|
||||
|
||||
### Боковая панель
|
||||
|
||||
- Информация о сервере (PID, CPU, память)
|
||||
- Список доступных парсеров
|
||||
- Статус подключения к API
|
||||
|
||||
## Использование
|
||||
|
||||
### 1. Загрузка файлов
|
||||
|
||||
1. Выберите соответствующий тип файла
|
||||
2. Нажмите "Загрузить"
|
||||
3. Дождитесь подтверждения загрузки
|
||||
|
||||
### 2. Получение данных
|
||||
|
||||
1. Выберите нужные параметры (ОГ, коды, столбцы)
|
||||
2. Нажмите "Получить данные"
|
||||
3. Результат отобразится в JSON формате
|
||||
|
||||
### 3. Мониторинг
|
||||
|
||||
- Проверяйте статус API в верхней части
|
||||
- Следите за логами операций
|
||||
- Используйте индикаторы загрузки
|
||||
|
||||
## Устранение неполадок
|
||||
|
||||
### API недоступен
|
||||
|
||||
```bash
|
||||
# Проверьте, запущен ли FastAPI сервер
|
||||
curl http://localhost:8000/
|
||||
|
||||
# Проверьте порт
|
||||
netstat -an | grep 8000
|
||||
```
|
||||
|
||||
### Streamlit не запускается
|
||||
|
||||
```bash
|
||||
# Проверьте версию Python
|
||||
python --version
|
||||
|
||||
# Переустановите Streamlit
|
||||
pip uninstall streamlit
|
||||
pip install streamlit
|
||||
|
||||
# Проверьте порт 8501
|
||||
netstat -an | grep 8501
|
||||
```
|
||||
|
||||
### Ошибки загрузки файлов
|
||||
|
||||
- Убедитесь, что файл соответствует формату
|
||||
- Проверьте размер файла (не более 100MB)
|
||||
- Убедитесь, что MinIO запущен
|
||||
|
||||
## Разработка
|
||||
|
||||
### Добавление новых функций
|
||||
|
||||
1. Создайте новую вкладку в `streamlit_app.py`
|
||||
2. Добавьте соответствующие API вызовы
|
||||
3. Обновите боковую панель при необходимости
|
||||
|
||||
### Кастомизация темы
|
||||
|
||||
Отредактируйте `.streamlit/config.toml`:
|
||||
```toml
|
||||
[theme]
|
||||
primaryColor = "#FF4B4B"
|
||||
backgroundColor = "#FFFFFF"
|
||||
# ... другие цвета
|
||||
```
|
||||
|
||||
### Добавление новых парсеров
|
||||
|
||||
1. Создайте парсер в `adapters/parsers/`
|
||||
2. Добавьте в `main.py`
|
||||
3. Обновите Streamlit интерфейс
|
||||
|
||||
## Безопасность
|
||||
|
||||
⚠️ **Внимание**: Приложение настроено для локальной разработки
|
||||
- CORS отключен
|
||||
- XSRF защита отключена
|
||||
- Не используйте в продакшене без дополнительной настройки
|
||||
|
||||
## Поддержка
|
||||
|
||||
При возникновении проблем:
|
||||
1. Проверьте логи в терминале
|
||||
2. Убедитесь, что все сервисы запущены
|
||||
3. Проверьте конфигурацию
|
||||
4. Обратитесь к документации API: http://localhost:8000/docs
|
||||
@@ -94,7 +94,8 @@ class MonitoringFuelParser(ParserPort):
|
||||
file_path,
|
||||
sheet_name=sheet,
|
||||
header=None,
|
||||
nrows=max_rows
|
||||
nrows=max_rows,
|
||||
engine='openpyxl'
|
||||
)
|
||||
|
||||
# Ищем строку, где хотя бы в одном столбце встречается искомое значение
|
||||
@@ -116,7 +117,8 @@ class MonitoringFuelParser(ParserPort):
|
||||
sheet_name=sheet,
|
||||
header=header_num,
|
||||
usecols=None,
|
||||
index_col=None
|
||||
index_col=None,
|
||||
engine='openpyxl'
|
||||
)
|
||||
|
||||
# === Удаление полностью пустых столбцов ===
|
||||
|
||||
@@ -44,6 +44,10 @@ class SvodkaCAParser(ParserPort):
|
||||
|
||||
def parse_svodka_ca(self, file_path: str, params: dict) -> dict:
|
||||
"""Парсинг сводки СА"""
|
||||
# Получаем параметры из params
|
||||
sheet_name = params.get('sheet_name', 0) # По умолчанию первый лист
|
||||
inclusion_list = params.get('inclusion_list', {'ТиП', 'Топливо', 'Потери'})
|
||||
|
||||
# === Извлечение и фильтрация ===
|
||||
tables = self.extract_all_tables(file_path, sheet_name)
|
||||
|
||||
@@ -150,8 +154,8 @@ class SvodkaCAParser(ParserPort):
|
||||
return None
|
||||
|
||||
def extract_all_tables(self, file_path, sheet_name=0):
|
||||
"""Извлекает все таблицы из Excel файла"""
|
||||
df = pd.read_excel(file_path, sheet_name=sheet_name, header=None)
|
||||
"""Извлечение всех таблиц из Excel файла"""
|
||||
df = pd.read_excel(file_path, sheet_name=sheet_name, header=None, engine='openpyxl')
|
||||
df_filled = df.fillna('')
|
||||
df_clean = df_filled.astype(str).replace(r'^\s*$', '', regex=True)
|
||||
|
||||
|
||||
@@ -70,7 +70,8 @@ class SvodkaPMParser(ParserPort):
|
||||
file,
|
||||
sheet_name=sheet,
|
||||
header=None,
|
||||
nrows=max_rows
|
||||
nrows=max_rows,
|
||||
engine='openpyxl'
|
||||
)
|
||||
|
||||
# Ищем строку, где хотя бы в одном столбце встречается искомое значение
|
||||
@@ -94,6 +95,7 @@ class SvodkaPMParser(ParserPort):
|
||||
header=header_num,
|
||||
usecols=None,
|
||||
nrows=2,
|
||||
engine='openpyxl'
|
||||
)
|
||||
|
||||
if df_probe.shape[0] == 0:
|
||||
@@ -115,7 +117,8 @@ class SvodkaPMParser(ParserPort):
|
||||
sheet_name=sheet,
|
||||
header=header_num,
|
||||
usecols=None,
|
||||
index_col=None
|
||||
index_col=None,
|
||||
engine='openpyxl'
|
||||
)
|
||||
|
||||
if indicator_col_name not in df_full.columns:
|
||||
|
||||
@@ -400,40 +400,40 @@ async def get_svodka_pm_total_ogs(
|
||||
raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
|
||||
|
||||
|
||||
# @app.post("/svodka_pm/get_data", tags=[SvodkaPMParser.name])
|
||||
# async def get_svodka_pm_data(
|
||||
# request_data: dict
|
||||
# ):
|
||||
# report_service = get_report_service()
|
||||
# """
|
||||
# Получение данных из отчета сводки факта СарНПЗ
|
||||
@app.post("/svodka_pm/get_data", tags=[SvodkaPMParser.name])
|
||||
async def get_svodka_pm_data(
|
||||
request_data: dict
|
||||
):
|
||||
report_service = get_report_service()
|
||||
"""
|
||||
Получение данных из отчета сводки факта СарНПЗ
|
||||
|
||||
# - indicator_id: ID индикатора
|
||||
# - code: Код для поиска
|
||||
# - search_value: Опциональное значение для поиска
|
||||
# """
|
||||
# try:
|
||||
# # Создаем запрос
|
||||
# request = DataRequest(
|
||||
# report_type='svodka_pm',
|
||||
# get_params=request_data
|
||||
# )
|
||||
- indicator_id: ID индикатора
|
||||
- code: Код для поиска
|
||||
- search_value: Опциональное значение для поиска
|
||||
"""
|
||||
try:
|
||||
# Создаем запрос
|
||||
request = DataRequest(
|
||||
report_type='svodka_pm',
|
||||
get_params=request_data
|
||||
)
|
||||
|
||||
# # Получаем данные
|
||||
# result = report_service.get_data(request)
|
||||
# Получаем данные
|
||||
result = report_service.get_data(request)
|
||||
|
||||
# if result.success:
|
||||
# return {
|
||||
# "success": True,
|
||||
# "data": result.data
|
||||
# }
|
||||
# else:
|
||||
# raise HTTPException(status_code=404, detail=result.message)
|
||||
if result.success:
|
||||
return {
|
||||
"success": True,
|
||||
"data": result.data
|
||||
}
|
||||
else:
|
||||
raise HTTPException(status_code=404, detail=result.message)
|
||||
|
||||
# except HTTPException:
|
||||
# raise
|
||||
# except Exception as e:
|
||||
# raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
|
||||
|
||||
|
||||
@app.post("/svodka_ca/upload", tags=[SvodkaCAParser.name],
|
||||
@@ -610,38 +610,38 @@ async def get_svodka_ca_data(
|
||||
# raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
|
||||
|
||||
|
||||
# @app.post("/monitoring_fuel/get_data", tags=[MonitoringFuelParser.name])
|
||||
# async def get_monitoring_fuel_data(
|
||||
# request_data: dict
|
||||
# ):
|
||||
# report_service = get_report_service()
|
||||
# """
|
||||
# Получение данных из отчета мониторинга топлива
|
||||
@app.post("/monitoring_fuel/get_data", tags=[MonitoringFuelParser.name])
|
||||
async def get_monitoring_fuel_data(
|
||||
request_data: dict
|
||||
):
|
||||
report_service = get_report_service()
|
||||
"""
|
||||
Получение данных из отчета мониторинга топлива
|
||||
|
||||
# - column: Название колонки для агрегации (normativ, total, total_svod)
|
||||
# """
|
||||
# try:
|
||||
# # Создаем запрос
|
||||
# request = DataRequest(
|
||||
# report_type='monitoring_fuel',
|
||||
# get_params=request_data
|
||||
# )
|
||||
- column: Название колонки для агрегации (normativ, total, total_svod)
|
||||
"""
|
||||
try:
|
||||
# Создаем запрос
|
||||
request = DataRequest(
|
||||
report_type='monitoring_fuel',
|
||||
get_params=request_data
|
||||
)
|
||||
|
||||
# # Получаем данные
|
||||
# result = report_service.get_data(request)
|
||||
# Получаем данные
|
||||
result = report_service.get_data(request)
|
||||
|
||||
# if result.success:
|
||||
# return {
|
||||
# "success": True,
|
||||
# "data": result.data
|
||||
# }
|
||||
# else:
|
||||
# raise HTTPException(status_code=404, detail=result.message)
|
||||
if result.success:
|
||||
return {
|
||||
"success": True,
|
||||
"data": result.data
|
||||
}
|
||||
else:
|
||||
raise HTTPException(status_code=404, detail=result.message)
|
||||
|
||||
# except HTTPException:
|
||||
# raise
|
||||
# except Exception as e:
|
||||
# raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
|
||||
except HTTPException:
|
||||
raise
|
||||
except Exception as e:
|
||||
raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")
|
||||
|
||||
|
||||
# @app.post("/monitoring_fuel/upload_directory", tags=[MonitoringFuelParser.name])
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1 +0,0 @@
|
||||
{"version":"1","format":"xl-single","id":"29118f57-702e-4363-9a41-9f06655e449d","xl":{"version":"3","this":"195a90f4-fc26-46a8-b6d4-0b50b99b1342","sets":[["195a90f4-fc26-46a8-b6d4-0b50b99b1342"]],"distributionAlgo":"SIPMOD+PARITY"}}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,396 +0,0 @@
|
||||
import streamlit as st
|
||||
import requests
|
||||
import json
|
||||
import pandas as pd
|
||||
import io
|
||||
import zipfile
|
||||
from typing import Dict, Any
|
||||
import os
|
||||
|
||||
# Конфигурация страницы
|
||||
st.set_page_config(
|
||||
page_title="NIN Excel Parsers API Demo",
|
||||
page_icon="📊",
|
||||
layout="wide",
|
||||
initial_sidebar_state="expanded"
|
||||
)
|
||||
|
||||
# Конфигурация API
|
||||
API_BASE_URL = os.getenv("API_BASE_URL", "http://localhost:8000")
|
||||
|
||||
def check_api_health():
|
||||
"""Проверка доступности API"""
|
||||
try:
|
||||
response = requests.get(f"{API_BASE_URL}/", timeout=5)
|
||||
return response.status_code == 200
|
||||
except:
|
||||
return False
|
||||
|
||||
def get_available_parsers():
|
||||
"""Получение списка доступных парсеров"""
|
||||
try:
|
||||
response = requests.get(f"{API_BASE_URL}/parsers")
|
||||
if response.status_code == 200:
|
||||
return response.json()["parsers"]
|
||||
return []
|
||||
except:
|
||||
return []
|
||||
|
||||
def get_server_info():
|
||||
"""Получение информации о сервере"""
|
||||
try:
|
||||
response = requests.get(f"{API_BASE_URL}/server-info")
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
return {}
|
||||
except:
|
||||
return {}
|
||||
|
||||
def upload_file_to_api(endpoint: str, file_data: bytes, filename: str):
|
||||
"""Загрузка файла на API"""
|
||||
try:
|
||||
files = {"zip_file": (filename, file_data, "application/zip")}
|
||||
response = requests.post(f"{API_BASE_URL}{endpoint}", files=files)
|
||||
return response.json(), response.status_code
|
||||
except Exception as e:
|
||||
return {"error": str(e)}, 500
|
||||
|
||||
def make_api_request(endpoint: str, data: Dict[str, Any]):
|
||||
"""Выполнение API запроса"""
|
||||
try:
|
||||
response = requests.post(f"{API_BASE_URL}{endpoint}", json=data)
|
||||
return response.json(), response.status_code
|
||||
except Exception as e:
|
||||
return {"error": str(e)}, 500
|
||||
|
||||
def main():
|
||||
st.title("🚀 NIN Excel Parsers API - Демонстрация")
|
||||
st.markdown("---")
|
||||
|
||||
# Проверка доступности API
|
||||
if not check_api_health():
|
||||
st.error(f"❌ API недоступен по адресу {API_BASE_URL}")
|
||||
st.info("Убедитесь, что FastAPI сервер запущен")
|
||||
return
|
||||
|
||||
st.success(f"✅ API доступен по адресу {API_BASE_URL}")
|
||||
|
||||
# Боковая панель с информацией
|
||||
with st.sidebar:
|
||||
st.header("ℹ️ Информация")
|
||||
|
||||
# Информация о сервере
|
||||
server_info = get_server_info()
|
||||
if server_info:
|
||||
st.subheader("Сервер")
|
||||
st.write(f"PID: {server_info.get('process_id', 'N/A')}")
|
||||
st.write(f"CPU ядер: {server_info.get('cpu_cores', 'N/A')}")
|
||||
st.write(f"Память: {server_info.get('memory_mb', 'N/A'):.1f} MB")
|
||||
|
||||
# Доступные парсеры
|
||||
parsers = get_available_parsers()
|
||||
if parsers:
|
||||
st.subheader("Доступные парсеры")
|
||||
for parser in parsers:
|
||||
st.write(f"• {parser}")
|
||||
|
||||
# Основные вкладки - по одной на каждый парсер
|
||||
tab1, tab2, tab3 = st.tabs([
|
||||
"📊 Сводки ПМ",
|
||||
"🏭 Сводки СА",
|
||||
"⛽ Мониторинг топлива"
|
||||
])
|
||||
|
||||
# Вкладка 1: Сводки ПМ - полный функционал
|
||||
with tab1:
|
||||
st.header("📊 Сводки ПМ - Полный функционал")
|
||||
|
||||
# Секция загрузки файлов
|
||||
st.subheader("📤 Загрузка файлов")
|
||||
uploaded_pm = st.file_uploader(
|
||||
"Выберите ZIP архив со сводками ПМ",
|
||||
type=['zip'],
|
||||
key="pm_upload"
|
||||
)
|
||||
|
||||
if uploaded_pm is not None:
|
||||
if st.button("📤 Загрузить сводки ПМ", key="upload_pm_btn"):
|
||||
with st.spinner("Загружаю файл..."):
|
||||
result, status = upload_file_to_api(
|
||||
"/svodka_pm/upload-zip",
|
||||
uploaded_pm.read(),
|
||||
uploaded_pm.name
|
||||
)
|
||||
|
||||
if status == 200:
|
||||
st.success(f"✅ {result.get('message', 'Файл загружен')}")
|
||||
st.info(f"ID объекта: {result.get('object_id', 'N/A')}")
|
||||
else:
|
||||
st.error(f"❌ Ошибка: {result.get('message', 'Неизвестная ошибка')}")
|
||||
|
||||
st.markdown("---")
|
||||
|
||||
# Секция получения данных
|
||||
st.subheader("🔍 Получение данных")
|
||||
|
||||
col1, col2 = st.columns(2)
|
||||
|
||||
with col1:
|
||||
st.subheader("Данные по одному ОГ")
|
||||
|
||||
og_id = st.selectbox(
|
||||
"Выберите ОГ",
|
||||
["SNPZ", "KNPZ", "ANHK", "AchNPZ", "UNPZ", "UNH", "NOV",
|
||||
"NovKuybNPZ", "KuybNPZ", "CyzNPZ", "TuapsNPZ", "RNPK",
|
||||
"NVNPO", "KLNPZ", "PurNP", "YANOS"],
|
||||
key="pm_single_og"
|
||||
)
|
||||
|
||||
codes = st.multiselect(
|
||||
"Выберите коды строк",
|
||||
[78, 79, 394, 395, 396, 397, 81, 82, 83, 84],
|
||||
default=[78, 79],
|
||||
key="pm_single_codes"
|
||||
)
|
||||
|
||||
columns = st.multiselect(
|
||||
"Выберите столбцы",
|
||||
["БП", "ПП", "СЭБ", "Факт", "План"],
|
||||
default=["БП", "ПП"],
|
||||
key="pm_single_columns"
|
||||
)
|
||||
|
||||
if st.button("🔍 Получить данные по ОГ", key="pm_single_btn"):
|
||||
if codes and columns:
|
||||
with st.spinner("Получаю данные..."):
|
||||
data = {
|
||||
"id": og_id,
|
||||
"codes": codes,
|
||||
"columns": columns
|
||||
}
|
||||
|
||||
result, status = make_api_request("/svodka_pm/get_single_og", data)
|
||||
|
||||
if status == 200:
|
||||
st.success("✅ Данные получены")
|
||||
st.json(result)
|
||||
else:
|
||||
st.error(f"❌ Ошибка: {result.get('message', 'Неизвестная ошибка')}")
|
||||
else:
|
||||
st.warning("⚠️ Выберите коды и столбцы")
|
||||
|
||||
with col2:
|
||||
st.subheader("Данные по всем ОГ")
|
||||
|
||||
codes_total = st.multiselect(
|
||||
"Выберите коды строк",
|
||||
[78, 79, 394, 395, 396, 397, 81, 82, 83, 84],
|
||||
default=[78, 79, 394, 395],
|
||||
key="pm_total_codes"
|
||||
)
|
||||
|
||||
columns_total = st.multiselect(
|
||||
"Выберите столбцы",
|
||||
["БП", "ПП", "СЭБ", "Факт", "План"],
|
||||
default=["БП", "ПП", "СЭБ"],
|
||||
key="pm_total_columns"
|
||||
)
|
||||
|
||||
if st.button("🔍 Получить данные по всем ОГ", key="pm_total_btn"):
|
||||
if codes_total and columns_total:
|
||||
with st.spinner("Получаю данные..."):
|
||||
data = {
|
||||
"codes": codes_total,
|
||||
"columns": columns_total
|
||||
}
|
||||
|
||||
result, status = make_api_request("/svodka_pm/get_total_ogs", data)
|
||||
|
||||
if status == 200:
|
||||
st.success("✅ Данные получены")
|
||||
st.json(result)
|
||||
else:
|
||||
st.error(f"❌ Ошибка: {result.get('message', 'Неизвестная ошибка')}")
|
||||
else:
|
||||
st.warning("⚠️ Выберите коды и столбцы")
|
||||
|
||||
# Вкладка 2: Сводки СА - полный функционал
|
||||
with tab2:
|
||||
st.header("🏭 Сводки СА - Полный функционал")
|
||||
|
||||
# Секция загрузки файлов
|
||||
st.subheader("📤 Загрузка файлов")
|
||||
uploaded_ca = st.file_uploader(
|
||||
"Выберите Excel файл сводки СА",
|
||||
type=['xlsx', 'xlsm', 'xls'],
|
||||
key="ca_upload"
|
||||
)
|
||||
|
||||
if uploaded_ca is not None:
|
||||
if st.button("📤 Загрузить сводку СА", key="upload_ca_btn"):
|
||||
with st.spinner("Загружаю файл..."):
|
||||
try:
|
||||
files = {"file": (uploaded_ca.name, uploaded_ca.read(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")}
|
||||
response = requests.post(f"{API_BASE_URL}/svodka_ca/upload", files=files)
|
||||
result = response.json()
|
||||
|
||||
if response.status_code == 200:
|
||||
st.success(f"✅ {result.get('message', 'Файл загружен')}")
|
||||
st.info(f"ID объекта: {result.get('object_id', 'N/A')}")
|
||||
else:
|
||||
st.error(f"❌ Ошибка: {result.get('message', 'Неизвестная ошибка')}")
|
||||
except Exception as e:
|
||||
st.error(f"❌ Ошибка: {str(e)}")
|
||||
|
||||
st.markdown("---")
|
||||
|
||||
# Секция получения данных
|
||||
st.subheader("🔍 Получение данных")
|
||||
|
||||
col1, col2 = st.columns(2)
|
||||
|
||||
with col1:
|
||||
st.subheader("Параметры запроса")
|
||||
|
||||
modes = st.multiselect(
|
||||
"Выберите режимы",
|
||||
["plan", "fact", "normativ"],
|
||||
default=["plan", "fact"],
|
||||
key="ca_modes"
|
||||
)
|
||||
|
||||
tables = st.multiselect(
|
||||
"Выберите таблицы",
|
||||
["ТиП", "Топливо", "Потери"],
|
||||
default=["ТиП", "Топливо"],
|
||||
key="ca_tables"
|
||||
)
|
||||
|
||||
with col2:
|
||||
st.subheader("Результат")
|
||||
if st.button("🔍 Получить данные СА", key="ca_btn"):
|
||||
if modes and tables:
|
||||
with st.spinner("Получаю данные..."):
|
||||
data = {
|
||||
"modes": modes,
|
||||
"tables": tables
|
||||
}
|
||||
|
||||
result, status = make_api_request("/svodka_ca/get_data", data)
|
||||
|
||||
if status == 200:
|
||||
st.success("✅ Данные получены")
|
||||
st.json(result)
|
||||
else:
|
||||
st.error(f"❌ Ошибка: {result.get('message', 'Неизвестная ошибка')}")
|
||||
else:
|
||||
st.warning("⚠️ Выберите режимы и таблицы")
|
||||
|
||||
# Вкладка 3: Мониторинг топлива - полный функционал
|
||||
with tab3:
|
||||
st.header("⛽ Мониторинг топлива - Полный функционал")
|
||||
|
||||
# Секция загрузки файлов
|
||||
st.subheader("📤 Загрузка файлов")
|
||||
uploaded_fuel = st.file_uploader(
|
||||
"Выберите ZIP архив с мониторингом топлива",
|
||||
type=['zip'],
|
||||
key="fuel_upload"
|
||||
)
|
||||
|
||||
if uploaded_fuel is not None:
|
||||
if st.button("📤 Загрузить мониторинг топлива", key="upload_fuel_btn"):
|
||||
with st.spinner("Загружаю файл..."):
|
||||
result, status = upload_file_to_api(
|
||||
"/monitoring_fuel/upload-zip",
|
||||
uploaded_fuel.read(),
|
||||
uploaded_fuel.name
|
||||
)
|
||||
|
||||
if status == 200:
|
||||
st.success(f"✅ {result.get('message', 'Файл загружен')}")
|
||||
st.info(f"ID объекта: {result.get('object_id', 'N/A')}")
|
||||
else:
|
||||
st.error(f"❌ Ошибка: {result.get('message', 'Неизвестная ошибка')}")
|
||||
|
||||
st.markdown("---")
|
||||
|
||||
# Секция получения данных
|
||||
st.subheader("🔍 Получение данных")
|
||||
|
||||
col1, col2 = st.columns(2)
|
||||
|
||||
with col1:
|
||||
st.subheader("Агрегация по колонкам")
|
||||
|
||||
columns_fuel = st.multiselect(
|
||||
"Выберите столбцы",
|
||||
["normativ", "total", "total_1"],
|
||||
default=["normativ", "total"],
|
||||
key="fuel_columns"
|
||||
)
|
||||
|
||||
if st.button("🔍 Получить агрегированные данные", key="fuel_total_btn"):
|
||||
if columns_fuel:
|
||||
with st.spinner("Получаю данные..."):
|
||||
data = {
|
||||
"columns": columns_fuel
|
||||
}
|
||||
|
||||
result, status = make_api_request("/monitoring_fuel/get_total_by_columns", data)
|
||||
|
||||
if status == 200:
|
||||
st.success("✅ Данные получены")
|
||||
st.json(result)
|
||||
else:
|
||||
st.error(f"❌ Ошибка: {result.get('message', 'Неизвестная ошибка')}")
|
||||
else:
|
||||
st.warning("⚠️ Выберите столбцы")
|
||||
|
||||
with col2:
|
||||
st.subheader("Данные за месяц")
|
||||
|
||||
month = st.selectbox(
|
||||
"Выберите месяц",
|
||||
[f"{i:02d}" for i in range(1, 13)],
|
||||
key="fuel_month"
|
||||
)
|
||||
|
||||
if st.button("🔍 Получить данные за месяц", key="fuel_month_btn"):
|
||||
with st.spinner("Получаю данные..."):
|
||||
data = {
|
||||
"month": month
|
||||
}
|
||||
|
||||
result, status = make_api_request("/monitoring_fuel/get_month_by_code", data)
|
||||
|
||||
if status == 200:
|
||||
st.success("✅ Данные получены")
|
||||
st.json(result)
|
||||
else:
|
||||
st.error(f"❌ Ошибка: {result.get('message', 'Неизвестная ошибка')}")
|
||||
|
||||
# Футер
|
||||
st.markdown("---")
|
||||
st.markdown("### 📚 Документация API")
|
||||
st.markdown(f"Полная документация доступна по адресу: {API_BASE_URL}/docs")
|
||||
|
||||
# Информация о проекте
|
||||
with st.expander("ℹ️ О проекте"):
|
||||
st.markdown("""
|
||||
**NIN Excel Parsers API** - это веб-сервис для парсинга и обработки Excel-файлов нефтеперерабатывающих заводов.
|
||||
|
||||
**Возможности:**
|
||||
- 📊 Парсинг сводок ПМ (план и факт)
|
||||
- 🏭 Парсинг сводок СА
|
||||
- ⛽ Мониторинг топлива
|
||||
|
||||
**Технологии:**
|
||||
- FastAPI
|
||||
- Pandas
|
||||
- MinIO (S3-совместимое хранилище)
|
||||
- Streamlit (веб-интерфейс)
|
||||
""")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -35,6 +35,7 @@ def main():
|
||||
|
||||
print("\n🚀 Запускаю Streamlit...")
|
||||
print("📍 URL: http://localhost:8501")
|
||||
print("🔗 API: http://localhost:8000")
|
||||
print("🛑 Для остановки нажмите Ctrl+C")
|
||||
|
||||
# Открываем браузер
|
||||
@@ -44,7 +45,11 @@ def main():
|
||||
except Exception as e:
|
||||
print(f"⚠️ Не удалось открыть браузер: {e}")
|
||||
|
||||
# Запускаем Streamlit
|
||||
# Запускаем Streamlit с правильными переменными окружения
|
||||
env = os.environ.copy()
|
||||
env["DOCKER_ENV"] = "false" # Локальный запуск
|
||||
env["API_BASE_URL"] = "http://localhost:8000" # Локальный API
|
||||
|
||||
try:
|
||||
subprocess.run([
|
||||
sys.executable, "-m", "streamlit", "run", "app.py",
|
||||
@@ -52,7 +57,7 @@ def main():
|
||||
"--server.address", "localhost",
|
||||
"--server.headless", "false",
|
||||
"--browser.gatherUsageStats", "false"
|
||||
])
|
||||
], env=env)
|
||||
except KeyboardInterrupt:
|
||||
print("\n👋 Streamlit остановлен")
|
||||
|
||||
@@ -15,8 +15,17 @@ st.set_page_config(
|
||||
initial_sidebar_state="expanded"
|
||||
)
|
||||
|
||||
# Конфигурация API - используем переменную окружения или значение по умолчанию
|
||||
API_BASE_URL = os.getenv("API_BASE_URL", "http://fastapi:8000")
|
||||
# Конфигурация API - автоматически определяем правильный адрес
|
||||
def get_api_base_url():
|
||||
"""Автоматически определяет правильный адрес API"""
|
||||
# Если запущено в Docker, используем внутренний адрес
|
||||
if os.getenv("DOCKER_ENV") == "true":
|
||||
return "http://fastapi:8000"
|
||||
|
||||
# Если запущено локально, используем localhost
|
||||
return "http://localhost:8000"
|
||||
|
||||
API_BASE_URL = os.getenv("API_BASE_URL", get_api_base_url())
|
||||
|
||||
def check_api_health():
|
||||
"""Проверка доступности API"""
|
||||
@@ -272,7 +281,7 @@ def main():
|
||||
st.markdown("---")
|
||||
|
||||
# Секция получения данных
|
||||
st.subheader("🔍 Получение данных")
|
||||
st.subheader("<EFBFBD><EFBFBD> Получение данных")
|
||||
|
||||
# Показываем доступные геттеры
|
||||
if getters_info and "getters" in getters_info:
|
||||
@@ -290,8 +299,8 @@ def main():
|
||||
|
||||
modes = st.multiselect(
|
||||
"Выберите режимы",
|
||||
["План", "Факт", "Норматив"],
|
||||
default=["План", "Факт"],
|
||||
["plan", "fact", "normativ"],
|
||||
default=["plan", "fact"],
|
||||
key="ca_modes"
|
||||
)
|
||||
|
||||
@@ -319,7 +328,7 @@ def main():
|
||||
st.success("✅ Данные получены")
|
||||
st.json(result)
|
||||
else:
|
||||
st.error(f"❌ Ошибка: {result.get('message', 'Неизвестная ошибка')}")
|
||||
st.error(f"❌ Ошибка: {result.get('message', f'Неизвестная ошибка: {status}')}")
|
||||
else:
|
||||
st.warning("⚠️ Выберите режимы и таблицы")
|
||||
|
||||
84
test_api.py
Normal file
84
test_api.py
Normal file
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Тестовый скрипт для проверки API
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
|
||||
def test_api_endpoints():
|
||||
"""Тестирование API эндпоинтов"""
|
||||
base_url = "http://localhost:8000"
|
||||
|
||||
print("🧪 ТЕСТИРОВАНИЕ API")
|
||||
print("=" * 50)
|
||||
|
||||
# Тест 1: Проверка доступности API
|
||||
print("\n1️⃣ Проверка доступности API...")
|
||||
try:
|
||||
response = requests.get(f"{base_url}/")
|
||||
if response.status_code == 200:
|
||||
print(f"✅ API доступен: {response.json()}")
|
||||
else:
|
||||
print(f"❌ API недоступен: {response.status_code}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка подключения к API: {e}")
|
||||
return False
|
||||
|
||||
# Тест 2: Список парсеров
|
||||
print("\n2️⃣ Получение списка парсеров...")
|
||||
try:
|
||||
response = requests.get(f"{base_url}/parsers")
|
||||
if response.status_code == 200:
|
||||
parsers = response.json()
|
||||
print(f"✅ Парсеры: {parsers}")
|
||||
else:
|
||||
print(f"❌ Ошибка получения парсеров: {response.status_code}")
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка: {e}")
|
||||
|
||||
# Тест 3: Информация о геттерах
|
||||
print("\n3️⃣ Информация о геттерах парсеров...")
|
||||
parsers_to_test = ["svodka_pm", "svodka_ca", "monitoring_fuel"]
|
||||
|
||||
for parser in parsers_to_test:
|
||||
try:
|
||||
response = requests.get(f"{base_url}/parsers/{parser}/getters")
|
||||
if response.status_code == 200:
|
||||
getters = response.json()
|
||||
print(f"✅ {parser}: {len(getters.get('getters', {}))} геттеров")
|
||||
else:
|
||||
print(f"❌ {parser}: ошибка {response.status_code}")
|
||||
except Exception as e:
|
||||
print(f"❌ {parser}: ошибка {e}")
|
||||
|
||||
# Тест 4: Загрузка тестового файла
|
||||
print("\n4️⃣ Тест загрузки файла...")
|
||||
try:
|
||||
# Создаем простой Excel файл для теста
|
||||
test_data = b"test content"
|
||||
files = {"file": ("test.xlsx", test_data, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")}
|
||||
|
||||
response = requests.post(f"{base_url}/svodka_ca/upload", files=files)
|
||||
print(f"📤 Результат загрузки: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
print(f"✅ Файл загружен: {result}")
|
||||
else:
|
||||
print(f"❌ Ошибка загрузки: {response.status_code}")
|
||||
try:
|
||||
error_detail = response.json()
|
||||
print(f"📋 Детали ошибки: {error_detail}")
|
||||
except:
|
||||
print(f"📋 Текст ошибки: {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка теста загрузки: {e}")
|
||||
|
||||
print("\n🎯 Тестирование завершено!")
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_api_endpoints()
|
||||
79
test_api_direct.py
Normal file
79
test_api_direct.py
Normal file
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Прямое тестирование API эндпоинтов
|
||||
"""
|
||||
|
||||
import requests
|
||||
import json
|
||||
|
||||
def test_api_endpoints():
|
||||
"""Тестирование API эндпоинтов"""
|
||||
base_url = "http://localhost:8000"
|
||||
|
||||
print("🧪 ПРЯМОЕ ТЕСТИРОВАНИЕ API")
|
||||
print("=" * 40)
|
||||
|
||||
# Тест 1: Проверка доступности API
|
||||
print("\n1️⃣ Проверка доступности API...")
|
||||
try:
|
||||
response = requests.get(f"{base_url}/")
|
||||
print(f"✅ API доступен: {response.status_code}")
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка: {e}")
|
||||
return
|
||||
|
||||
# Тест 2: Тестирование эндпоинта svodka_ca/get_data
|
||||
print("\n2️⃣ Тестирование svodka_ca/get_data...")
|
||||
try:
|
||||
data = {
|
||||
"getter": "get_data",
|
||||
"modes": ["plan", "fact"],
|
||||
"tables": ["ТиП", "Топливо"]
|
||||
}
|
||||
|
||||
response = requests.post(f"{base_url}/svodka_ca/get_data", json=data)
|
||||
print(f"📥 Результат: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
print(f"✅ Успешно: {result}")
|
||||
else:
|
||||
try:
|
||||
error_detail = response.json()
|
||||
print(f"❌ Ошибка: {error_detail}")
|
||||
except:
|
||||
print(f"❌ Ошибка: {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Исключение: {e}")
|
||||
|
||||
# Тест 3: Тестирование эндпоинта svodka_pm/get_data
|
||||
print("\n3️⃣ Тестирование svodka_pm/get_data...")
|
||||
try:
|
||||
data = {
|
||||
"getter": "single_og",
|
||||
"id": "SNPZ",
|
||||
"codes": [78, 79],
|
||||
"columns": ["БП", "ПП"]
|
||||
}
|
||||
|
||||
response = requests.post(f"{base_url}/svodka_pm/get_data", json=data)
|
||||
print(f"📥 Результат: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
print(f"✅ Успешно: {result}")
|
||||
else:
|
||||
try:
|
||||
error_detail = response.json()
|
||||
print(f"❌ Ошибка: {error_detail}")
|
||||
except:
|
||||
print(f"❌ Ошибка: {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Исключение: {e}")
|
||||
|
||||
print("\n🎯 Тестирование завершено!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_api_endpoints()
|
||||
96
test_ca_workflow.py
Normal file
96
test_ca_workflow.py
Normal file
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Тестирование полного workflow с сводкой СА
|
||||
"""
|
||||
|
||||
import requests
|
||||
import os
|
||||
import time
|
||||
|
||||
def test_ca_workflow():
|
||||
"""Тестирование полного workflow с сводкой СА"""
|
||||
base_url = "http://localhost:8000"
|
||||
test_file = "python_parser/data/svodka_ca.xlsx"
|
||||
|
||||
print("🧪 ТЕСТ ПОЛНОГО WORKFLOW СВОДКИ СА")
|
||||
print("=" * 50)
|
||||
|
||||
# Проверяем, что файл существует
|
||||
if not os.path.exists(test_file):
|
||||
print(f"❌ Файл {test_file} не найден")
|
||||
return False
|
||||
|
||||
print(f"📁 Тестовый файл найден: {test_file}")
|
||||
print(f"📏 Размер: {os.path.getsize(test_file)} байт")
|
||||
|
||||
# Шаг 1: Загружаем файл
|
||||
print("\n1️⃣ Загружаю файл сводки СА...")
|
||||
try:
|
||||
with open(test_file, 'rb') as f:
|
||||
file_data = f.read()
|
||||
|
||||
files = {"file": ("svodka_ca.xlsx", file_data, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")}
|
||||
|
||||
response = requests.post(f"{base_url}/svodka_ca/upload", files=files)
|
||||
print(f"📤 Результат загрузки: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
print(f"✅ Файл загружен: {result}")
|
||||
object_id = result.get('object_id', 'nin_excel_data_svodka_ca')
|
||||
else:
|
||||
print(f"❌ Ошибка загрузки: {response.status_code}")
|
||||
try:
|
||||
error_detail = response.json()
|
||||
print(f"📋 Детали ошибки: {error_detail}")
|
||||
except:
|
||||
print(f"📋 Текст ошибки: {response.text}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка загрузки: {e}")
|
||||
return False
|
||||
|
||||
# Шаг 2: Получаем данные через геттер
|
||||
print("\n2️⃣ Получаю данные через геттер...")
|
||||
try:
|
||||
data = {
|
||||
"getter": "get_data",
|
||||
"modes": ["plan", "fact"], # Используем английские названия
|
||||
"tables": ["ТиП", "Топливо"]
|
||||
}
|
||||
|
||||
response = requests.post(f"{base_url}/svodka_ca/get_data", json=data)
|
||||
print(f"📥 Результат получения данных: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
print(f"✅ Данные получены успешно!")
|
||||
print(f"📊 Размер ответа: {len(str(result))} символов")
|
||||
|
||||
# Показываем структуру данных
|
||||
if isinstance(result, dict):
|
||||
print(f"🔍 Структура данных:")
|
||||
for key, value in result.items():
|
||||
if isinstance(value, dict):
|
||||
print(f" {key}: {len(value)} элементов")
|
||||
else:
|
||||
print(f" {key}: {type(value).__name__}")
|
||||
else:
|
||||
print(f"❌ Ошибка получения данных: {response.status_code}")
|
||||
try:
|
||||
error_detail = response.json()
|
||||
print(f"📋 Детали ошибки: {error_detail}")
|
||||
except:
|
||||
print(f"📋 Текст ошибки: {response.text}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Ошибка получения данных: {e}")
|
||||
return False
|
||||
|
||||
print("\n🎯 Тестирование завершено успешно!")
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_ca_workflow()
|
||||
110
test_minio_connection.py
Normal file
110
test_minio_connection.py
Normal file
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Тестовый скрипт для проверки подключения к MinIO
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import io
|
||||
from minio import Minio
|
||||
|
||||
def test_minio_connection():
|
||||
"""Тестирование подключения к MinIO"""
|
||||
print("🔍 Тестирование подключения к MinIO...")
|
||||
|
||||
# Параметры подключения
|
||||
endpoint = os.getenv("MINIO_ENDPOINT", "localhost:9000")
|
||||
access_key = os.getenv("MINIO_ACCESS_KEY", "minioadmin")
|
||||
secret_key = os.getenv("MINIO_SECRET_KEY", "minioadmin")
|
||||
bucket_name = os.getenv("MINIO_BUCKET", "svodka-data")
|
||||
|
||||
print(f"📍 Endpoint: {endpoint}")
|
||||
print(f"🔑 Access Key: {access_key}")
|
||||
print(f"🔐 Secret Key: {secret_key}")
|
||||
print(f"🪣 Bucket: {bucket_name}")
|
||||
|
||||
try:
|
||||
# Создаем клиент
|
||||
print("\n🚀 Создаю MinIO клиент...")
|
||||
client = Minio(
|
||||
endpoint,
|
||||
access_key=access_key,
|
||||
secret_key=secret_key,
|
||||
secure=False,
|
||||
cert_check=False
|
||||
)
|
||||
|
||||
# Проверяем подключение
|
||||
print("✅ MinIO клиент создан")
|
||||
|
||||
# Проверяем bucket
|
||||
print(f"\n🔍 Проверяю bucket '{bucket_name}'...")
|
||||
if client.bucket_exists(bucket_name):
|
||||
print(f"✅ Bucket '{bucket_name}' существует")
|
||||
else:
|
||||
print(f"⚠️ Bucket '{bucket_name}' не существует, создаю...")
|
||||
client.make_bucket(bucket_name)
|
||||
print(f"✅ Bucket '{bucket_name}' создан")
|
||||
|
||||
# Пробуем загрузить тестовый файл
|
||||
print("\n📤 Тестирую загрузку файла...")
|
||||
test_data = b"Hello MinIO!"
|
||||
test_stream = io.BytesIO(test_data)
|
||||
|
||||
client.put_object(
|
||||
bucket_name,
|
||||
"test.txt",
|
||||
test_stream,
|
||||
length=len(test_data),
|
||||
content_type='text/plain'
|
||||
)
|
||||
print("✅ Тестовый файл загружен")
|
||||
|
||||
# Пробуем скачать файл
|
||||
print("\n📥 Тестирую скачивание файла...")
|
||||
response = client.get_object(bucket_name, "test.txt")
|
||||
downloaded_data = response.read()
|
||||
print(f"✅ Файл скачан: {downloaded_data}")
|
||||
|
||||
# Удаляем тестовый файл
|
||||
client.remove_object(bucket_name, "test.txt")
|
||||
print("✅ Тестовый файл удален")
|
||||
|
||||
print("\n🎉 Все тесты MinIO прошли успешно!")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
print(f"\n❌ Ошибка подключения к MinIO: {e}")
|
||||
print(f"Тип ошибки: {type(e).__name__}")
|
||||
return False
|
||||
|
||||
def test_environment():
|
||||
"""Проверка переменных окружения"""
|
||||
print("🔧 Проверка переменных окружения:")
|
||||
env_vars = [
|
||||
"MINIO_ENDPOINT",
|
||||
"MINIO_ACCESS_KEY",
|
||||
"MINIO_SECRET_KEY",
|
||||
"MINIO_BUCKET"
|
||||
]
|
||||
|
||||
for var in env_vars:
|
||||
value = os.getenv(var, "НЕ УСТАНОВЛЕНО")
|
||||
print(f" {var}: {value}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("🧪 ТЕСТ ПОДКЛЮЧЕНИЯ К MINIO")
|
||||
print("=" * 60)
|
||||
|
||||
test_environment()
|
||||
print()
|
||||
|
||||
success = test_minio_connection()
|
||||
|
||||
if success:
|
||||
print("\n✅ MinIO работает корректно!")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("\n❌ Проблемы с MinIO!")
|
||||
sys.exit(1)
|
||||
69
test_upload.py
Normal file
69
test_upload.py
Normal file
@@ -0,0 +1,69 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Тестирование загрузки Excel файла
|
||||
"""
|
||||
|
||||
import requests
|
||||
import os
|
||||
|
||||
def test_file_upload():
|
||||
"""Тестирование загрузки файла"""
|
||||
base_url = "http://localhost:8000"
|
||||
filename = "test_file.xlsx"
|
||||
|
||||
print("🧪 ТЕСТ ЗАГРУЗКИ ФАЙЛА")
|
||||
print("=" * 40)
|
||||
|
||||
# Проверяем, что файл существует
|
||||
if not os.path.exists(filename):
|
||||
print(f"❌ Файл {filename} не найден")
|
||||
return False
|
||||
|
||||
print(f"📁 Файл найден: {filename}")
|
||||
print(f"📏 Размер: {os.path.getsize(filename)} байт")
|
||||
|
||||
# Тестируем загрузку в разные парсеры
|
||||
parsers = [
|
||||
("svodka_ca", "/svodka_ca/upload", "file"),
|
||||
("monitoring_fuel", "/monitoring_fuel/upload-zip", "zip_file"),
|
||||
("svodka_pm", "/svodka_pm/upload-zip", "zip_file")
|
||||
]
|
||||
|
||||
for parser_name, endpoint, file_param in parsers:
|
||||
print(f"\n🔍 Тестирую {parser_name}...")
|
||||
|
||||
try:
|
||||
# Читаем файл
|
||||
with open(filename, 'rb') as f:
|
||||
file_data = f.read()
|
||||
|
||||
# Определяем content type
|
||||
if filename.endswith('.xlsx'):
|
||||
content_type = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
else:
|
||||
content_type = "application/octet-stream"
|
||||
|
||||
# Загружаем файл с правильным параметром
|
||||
files = {file_param: (filename, file_data, content_type)}
|
||||
|
||||
response = requests.post(f"{base_url}{endpoint}", files=files)
|
||||
print(f"📤 Результат: {response.status_code}")
|
||||
|
||||
if response.status_code == 200:
|
||||
result = response.json()
|
||||
print(f"✅ Успешно: {result}")
|
||||
else:
|
||||
try:
|
||||
error_detail = response.json()
|
||||
print(f"❌ Ошибка: {error_detail}")
|
||||
except:
|
||||
print(f"❌ Ошибка: {response.text}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ Исключение: {e}")
|
||||
|
||||
print("\n🎯 Тестирование завершено!")
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_file_upload()
|
||||
Reference in New Issue
Block a user