350 lines
13 KiB
Markdown
350 lines
13 KiB
Markdown
# BI-дашборд для оценки эффективности автобаз
|
||
|
||
Корпоративный веб-сервис для мониторинга и анализа работы транспортных подразделений.
|
||
|
||
## 📋 Возможности
|
||
|
||
- **Многофакторный рейтинг** подразделений на основе 5 KPI
|
||
- **Авторизация и RBAC** (роли: админ, руководитель, бухгалтер, водитель)
|
||
- **Экспорт отчётов** для обоснования бюджета
|
||
- **Абстракция данных** (легко переключиться с Google Sheets на ERP API)
|
||
- **Политика конфиденциальности** на всех экранах
|
||
|
||
## 🏗 Архитектура
|
||
|
||
### Паттерн Repository (DataFetcher)
|
||
|
||
```
|
||
┌─────────────────────────────────────────┐
|
||
│ DataFetcher (Interface) │
|
||
│ - get_fleet_data() -> DataFrame │
|
||
│ - get_requests_data() -> DataFrame │
|
||
│ - get_accidents_data() -> DataFrame │
|
||
│ - get_maintenance_data() -> DataFrame │
|
||
│ - get_fuel_data() -> DataFrame │
|
||
│ - get_employees_data() -> DataFrame │
|
||
└─────────────────────────────────────────┘
|
||
▲
|
||
┌───────────┼───────────┐
|
||
│ │ │
|
||
┌───────┴────┐ ┌───┴────────┐ ┌┴──────────────┐
|
||
│MockData │ │GoogleSheets│ │ApiFetcher │
|
||
│Fetcher │ │Fetcher │ │(будущая ERP) │
|
||
└────────────┘ └────────────┘ └───────────────┘
|
||
```
|
||
|
||
**Преимущества:**
|
||
- Код бизнес-логики (`KPICalculator`) не зависит от источника данных
|
||
- Для подключения ERP нужно лишь создать класс `ApiFetcher` с теми же методами
|
||
- Тестирование на mock-данных без подключения к внешним сервисам
|
||
|
||
## 📁 Структура проекта
|
||
|
||
```
|
||
autobase-bi/
|
||
├── app.py # Главное приложение Streamlit
|
||
├── data_fetcher.py # Абстракция доступа к данным
|
||
├── requirements.txt # Зависимости Python
|
||
├── .env.example # Шаблон переменных окружения
|
||
├── service_account.json # Ключ Google Service Account (не в git!)
|
||
└── README.md # Эта документация
|
||
```
|
||
|
||
## 🚀 Быстрый старт
|
||
|
||
### 1. Установка зависимостей
|
||
|
||
```bash
|
||
pip install -r requirements.txt
|
||
```
|
||
|
||
### 2. Настройка окружения
|
||
|
||
Скопируйте `.env.example` в `.env`:
|
||
|
||
```bash
|
||
cp .env.example .env
|
||
```
|
||
|
||
Отредактируйте `.env` при необходимости (для демо можно оставить значения по умолчанию).
|
||
|
||
### 3. Запуск приложения
|
||
|
||
```bash
|
||
streamlit run app.py
|
||
```
|
||
|
||
Приложение откроется по адресу: http://localhost:8501
|
||
|
||
### 4. Вход в систему
|
||
|
||
Используйте тестовые учётные данные:
|
||
|
||
| Логин | Пароль | Роль | Права доступа |
|
||
|-------|--------|------|---------------|
|
||
| admin | admin123 | Администратор | Полный доступ |
|
||
| manager | manager123 | Руководитель | Просмотр + экспорт |
|
||
| accountant | accountant123 | Бухгалтер | Финансы + экспорт |
|
||
| driver | driver123 | Водитель | Только свои данные |
|
||
|
||
## 🔧 Настройка Google Sheets API
|
||
|
||
### Шаг 1: Создание сервиса в Google Cloud
|
||
|
||
1. Откройте [Google Cloud Console](https://console.cloud.google.com/)
|
||
2. Создайте новый проект или выберите существующий
|
||
3. Включите **Google Sheets API**:
|
||
- Перейдите в "APIs & Services" → "Library"
|
||
- Найдите "Google Sheets API" и нажмите "Enable"
|
||
|
||
### Шаг 2: Создание сервисного аккаунта
|
||
|
||
1. Перейдите в "APIs & Services" → "Credentials"
|
||
2. Нажмите "Create Credentials" → "Service Account"
|
||
3. Заполните данные:
|
||
- Service account name: `autobase-bi-service`
|
||
- Service account ID: создастся автоматически
|
||
- Role: **Viewer** (только чтение)
|
||
4. Нажмите "Create and Continue"
|
||
|
||
### Шаг 3: Генерация ключа
|
||
|
||
1. На странице сервисного аккаунта перейдите во вкладку "Keys"
|
||
2. "Add Key" → "Create new key"
|
||
3. Выберите формат **JSON**
|
||
4. Скачайте файл и сохраните как `service_account.json` в корне проекта
|
||
|
||
### Шаг 4: Предоставление доступа к таблице
|
||
|
||
1. Откройте вашу Google Таблицу
|
||
2. Нажмите кнопку "Поделиться" (Share)
|
||
3. Вставьте email сервисного аккаунта (вида `autobase-bi-service@project-id.iam.gserviceaccount.com`)
|
||
4. Дайте права **Читатель** (Viewer)
|
||
5. Скопируйте ID таблицы из URL (между `/d/` и `/edit`)
|
||
|
||
### Шаг 5: Настройка .env
|
||
|
||
Отредактируйте `.env`:
|
||
|
||
```env
|
||
GOOGLE_SERVICE_ACCOUNT_KEY_PATH=./service_account.json
|
||
GOOGLE_SHEET_ID=ваш_id_таблицы_здесь
|
||
```
|
||
|
||
### Шаг 6: Структура Google Таблицы
|
||
|
||
Создайте листы со следующими названиями и колонками:
|
||
|
||
**Лист "Автопарк":**
|
||
```
|
||
vehicle_id | vehicle_type | department | status | year | mileage | purchase_cost
|
||
V001 | Грузовой | Автобаза №1 | В работе | 2022 | 75000 | 2500000
|
||
```
|
||
|
||
**Лист "Заявки":**
|
||
```
|
||
request_id | department | date | status | fulfilled | vehicle_id
|
||
R0001 | Автобаза №1 | 2024-01-15 | Исполнено | 1 | V001
|
||
```
|
||
|
||
**Лист "ДТП_ПДД":**
|
||
```
|
||
incident_id | date | vehicle_id | driver_id | type | severity | fine_amount
|
||
I001 | 2024-02-10 | V001 | D005 | ДТП | Средний | 15000
|
||
```
|
||
|
||
**Лист "ТО_Ремонты":**
|
||
```
|
||
maintenance_id | vehicle_id | date | type | downtime_days | cost
|
||
M0001 | V001 | 2024-01-20 | Плановое ТО | 2 | 25000
|
||
```
|
||
|
||
**Лист "ГСМ_Финансы":**
|
||
```
|
||
vehicle_id | date | fuel_liters | fuel_cost | planned_cost | actual_cost
|
||
V001 | 2024-01-15 | 250 | 12500 | 12000 | 12500
|
||
```
|
||
|
||
**Лист "Сотрудники":**
|
||
```
|
||
employee_id | name | department | position | license_category
|
||
D001 | Иванов А.А. | Автобаза №1 | Водитель | C
|
||
```
|
||
|
||
### Шаг 7: Переключение на Google Sheets
|
||
|
||
В приложении выберите в боковой панели:
|
||
- Источник данных: **Google Sheets**
|
||
|
||
Или измените `app.py`:
|
||
|
||
```python
|
||
# Было (демо):
|
||
st.session_state.data_fetcher = MockDataFetcher()
|
||
|
||
# Стало (Google Sheets):
|
||
from data_fetcher import GoogleSheetsFetcher
|
||
st.session_state.data_fetcher = GoogleSheetsFetcher(
|
||
service_account_path=os.getenv('GOOGLE_SERVICE_ACCOUNT_KEY_PATH'),
|
||
sheet_id=os.getenv('GOOGLE_SHEET_ID')
|
||
)
|
||
```
|
||
|
||
## 📊 Метрики и KPI
|
||
|
||
### 1. Коэффициент исполнения заявок (25%)
|
||
```
|
||
Исполнено заявок / Всего заявок × 100%
|
||
```
|
||
|
||
### 2. Коэффициент использования парка (КИП) (20%)
|
||
```
|
||
(Всего ТС - В ремонте) / Всего ТС × 100%
|
||
```
|
||
|
||
### 3. Безопасность (20%)
|
||
```
|
||
100 - (ДТП × 5 + Нарушения × 2) / Всего ТС × 10
|
||
```
|
||
|
||
### 4. Эксплуатация и ремонты (15%)
|
||
```
|
||
100 - (Простой дней / Всего ТС) × 2
|
||
```
|
||
|
||
### 5. Финансы и ГСМ (20%)
|
||
```
|
||
Перерасход: (Факт - План) / План × 100%
|
||
Стоимость 1 км: Общие расходы / Пробег × 1000
|
||
```
|
||
|
||
### Сводный рейтинг
|
||
```
|
||
Рейтинг = KPI1×0.25 + KPI2×0.20 + KPI3×0.20 + KPI4×0.15 + KPI5×0.20
|
||
```
|
||
|
||
## 🔐 Безопасность
|
||
|
||
### Ролевая модель (RBAC)
|
||
|
||
| Роль | Просмотр всех | Экспорт | Финансы | Управление |
|
||
|------|---------------|---------|---------|------------|
|
||
| Администратор | ✅ | ✅ | ✅ | ✅ |
|
||
| Руководитель | ✅ | ✅ | ✅ | ❌ |
|
||
| Бухгалтер | ❌ | ✅ | ✅ | ❌ |
|
||
| Водитель | ❌ | ❌ | ❌ | ❌ |
|
||
|
||
### Продакшен-рекомендации
|
||
|
||
1. **Замените хардкод логинов** на LDAP/Active Directory
|
||
2. **Настройте HTTPS** через reverse proxy (nginx)
|
||
3. **Добавьте логирование** действий пользователей
|
||
4. **Регулярно ротируйте** токены и ключи
|
||
|
||
```python
|
||
# Пример LDAP авторизации (заменить в check_credentials)
|
||
import ldap
|
||
|
||
def ldap_auth(username, password):
|
||
conn = ldap.initialize('ldap://your-domain.local')
|
||
try:
|
||
conn.simple_bind_s(f'{username}@domain.local', password)
|
||
return True
|
||
except:
|
||
return False
|
||
```
|
||
|
||
## 📈 Экспорт отчёта для бюджета
|
||
|
||
1. Войдите под ролью **admin** или **manager**
|
||
2. Перейдите в раздел **"💰 Бюджет 2027"**
|
||
3. Нажмите **"📥 Скачать отчёт (CSV)"**
|
||
|
||
Отчёт содержит:
|
||
- Текущие расходы за 2024 год
|
||
- Прогноз на 2027 год с учётом инфляции и износа
|
||
- Рекомендуемый бюджет
|
||
- Потребность в обновлении автопарка
|
||
|
||
## 🔄 Подключение ERP API (Фаза 2)
|
||
|
||
Для перехода с Google Sheets на ERP:
|
||
|
||
### 1. Создайте класс ApiFetcher
|
||
|
||
```python
|
||
# data_fetcher.py
|
||
class ApiFetcher(DataFetcher):
|
||
def __init__(self, api_base_url: str, api_token: str):
|
||
self.api_base_url = api_base_url
|
||
self.api_token = api_token
|
||
|
||
def get_fleet_data(self) -> pd.DataFrame:
|
||
response = requests.get(
|
||
f'{self.api_base_url}/fleet/vehicles',
|
||
headers={'Authorization': f'Bearer {self.api_token}'}
|
||
)
|
||
return pd.DataFrame(response.json())
|
||
|
||
# Реализуйте остальные 5 методов аналогично
|
||
```
|
||
|
||
### 2. Обновите app.py
|
||
|
||
```python
|
||
from data_fetcher import ApiFetcher
|
||
|
||
st.session_state.data_fetcher = ApiFetcher(
|
||
api_base_url=os.getenv('ERP_API_URL'),
|
||
api_token=os.getenv('ERP_API_TOKEN')
|
||
)
|
||
```
|
||
|
||
### 3. Добавьте в .env
|
||
|
||
```env
|
||
ERP_API_URL=https://erp.your-company.local/api/v1
|
||
ERP_API_TOKEN=your_token_here
|
||
```
|
||
|
||
**Никаких изменений в бизнес-логике не требуется!**
|
||
|
||
## 🛠 Troubleshooting
|
||
|
||
### Ошибка "ModuleNotFoundError"
|
||
```bash
|
||
pip install -r requirements.txt --upgrade
|
||
```
|
||
|
||
### Ошибка Google API "credentials invalid"
|
||
- Проверьте путь к `service_account.json`
|
||
- Убедитесь, что сервисный аккаунт имеет доступ к таблице
|
||
- Проверьте ID таблицы в `.env`
|
||
|
||
### Приложение не запускается
|
||
```bash
|
||
# Проверьте версию Python (требуется 3.8+)
|
||
python --version
|
||
|
||
# Пересоздайте виртуальное окружение
|
||
python -m venv venv
|
||
source venv/bin/activate # Linux/Mac
|
||
# или
|
||
venv\Scripts\activate # Windows
|
||
pip install -r requirements.txt
|
||
```
|
||
|
||
### Streamlit показывает кэш
|
||
- Очистите кэш: `st.cache_resource.clear()`
|
||
- Перезапустите приложение
|
||
|
||
## 📄 Лицензия
|
||
|
||
Внутренний корпоративный сервис. Не для публикации.
|
||
|
||
---
|
||
|
||
**⚠️ Политика конфиденциальности:**
|
||
|
||
Внимание: информация носит строго конфиденциальный характер. Я передаю данные для обработки, но требую хранить их в безопасности и не передавать третьим лицам согласно закону. Запрещено размещение в открытом доступе.
|