mirror of
https://github.com/wasrusgen/zov-tech.git
synced 2026-06-03 15:44:47 +00:00
Backend: - assembler_parser.py: parse Excel «Таблица занятости сборщиков» - Handles both row-order variants (2026: dates row1; 2025-: dates row2) - Extracts amount from end of cell text, supports compound "6030+20100" - aggregate(): by_assembler×month + by_month totals - In-memory cache with mtime invalidation - main.py: /api/assembler_analytics — local file first, Drive fallback - LOCAL: /app/data/assembler_schedule.xlsx (mounted volume) - Config: ASSEMBLER_SCHEDULE_PATH env var override - config.py: assembler_schedule_file_id for Drive fallback - docker-compose.yml: /opt/zov-tech/data → /app/data:ro volume Frontend: - assembler_analytics.js: year filter, monthly table, assembler ranking with progress bars, per-order average, last-6-months breakdown - app.js: route #/admin/assembler-analytics + "Аналитика" button Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
67 lines
2.9 KiB
Python
67 lines
2.9 KiB
Python
"""Конфиг бэкенда — читается из переменных окружения."""
|
||
from __future__ import annotations
|
||
import os
|
||
from dataclasses import dataclass
|
||
from functools import lru_cache
|
||
|
||
|
||
@dataclass(frozen=True)
|
||
class Config:
|
||
bot_token: str
|
||
admin_tg_id: int
|
||
sheet_id: str
|
||
google_credentials_path: str
|
||
|
||
gigachat_auth_key: str
|
||
gigachat_model: str
|
||
gigachat_scope: str
|
||
|
||
active_period_days: int
|
||
grace_period_days: int
|
||
|
||
proxy6_token: str # пусто = без прокси (прямой HTTP)
|
||
proxy_static_list: str # статический список прокси через запятую: "http://user:pass@host:port,..."
|
||
proxy_list_file: str # путь к файлу со списком прокси в формате "host:port:user:pass" или "http://..."
|
||
|
||
# Внутренний секрет для вызовов бота → бэкенда (без initData)
|
||
internal_secret: str
|
||
|
||
# Google Drive ID файла ОТГРУЗКИ.xlsx (отгрузки с завода)
|
||
shipments_file_id: str
|
||
# Google Drive ID файла «Поступление заказов на склад СПб.xlsx»
|
||
arrivals_file_id: str
|
||
# Google Drive ID «Таблица занятости сборщиков.xlsx»
|
||
assembler_schedule_file_id: str
|
||
|
||
|
||
def _required(name: str) -> str:
|
||
val = os.getenv(name)
|
||
if not val:
|
||
raise RuntimeError(f"Missing required env var: {name}")
|
||
return val
|
||
|
||
|
||
@lru_cache(maxsize=1)
|
||
def get_config() -> Config:
|
||
return Config(
|
||
bot_token=_required("BOT_TOKEN"),
|
||
admin_tg_id=int(os.getenv("ADMIN_TG_ID", "0")),
|
||
sheet_id=_required("SHEET_ID"),
|
||
google_credentials_path=os.getenv("GOOGLE_CREDENTIALS_PATH", "/app/credentials.json"),
|
||
gigachat_auth_key=_required("GIGACHAT_AUTH_KEY"),
|
||
gigachat_model=os.getenv("GIGACHAT_MODEL", "GigaChat-Pro"),
|
||
gigachat_scope=os.getenv("GIGACHAT_SCOPE", "GIGACHAT_API_PERS"),
|
||
active_period_days=int(os.getenv("ACTIVE_PERIOD_DAYS", "90")),
|
||
grace_period_days=int(os.getenv("GRACE_PERIOD_DAYS", "14")),
|
||
proxy6_token=os.getenv("PROXY6_TOKEN", ""),
|
||
proxy_static_list=os.getenv("PROXY_STATIC_LIST", ""),
|
||
proxy_list_file=os.getenv("PROXY_LIST_FILE", ""),
|
||
internal_secret=os.getenv("INTERNAL_SECRET", ""),
|
||
# ОТГРУЗКИ — актуальный ID из AI АНАЛИТИКА/_sources_config.json
|
||
shipments_file_id=os.getenv("SHIPMENTS_FILE_ID", "1KCJUXjhVR2NWEz9bD0kjTaEADsxF8gI5GMzLwJ2bw84"),
|
||
# Поступление заказов на склад СПб — тот же файл что ОТГРУЗКИ
|
||
arrivals_file_id=os.getenv("ARRIVALS_FILE_ID", "1KCJUXjhVR2NWEz9bD0kjTaEADsxF8gI5GMzLwJ2bw84"),
|
||
# Таблица занятости сборщиков — передать file_id через env
|
||
assembler_schedule_file_id=os.getenv("ASSEMBLER_SCHEDULE_FILE_ID", ""),
|
||
)
|