from __future__ import annotations from pathlib import Path from typing import Any import os import shutil from datetime import datetime from dotenv import dotenv_values, load_dotenv, set_key from parsers import parser_sources PROJECT_ROOT = Path(__file__).resolve().parent.parent ENV_PATH = PROJECT_ROOT / ".env" def _field(key: str, label: str, value: str = "", *, field_type: str = "text", options: list[dict] | None = None, help_text: str = "", wide: bool = False) -> dict[str, Any]: return { "key": key, "label": label, "value": value or "", "type": field_type, "options": options or [], "help": help_text, "wide": wide, } def _source_field_key(source_key: str, field: str) -> str: return f"{source_key}_{field}" def get_env_file_path() -> Path: return ENV_PATH def _dotenv_values() -> dict[str, str]: if not ENV_PATH.exists(): return {} raw = dotenv_values(ENV_PATH) return {key: str(value or "") for key, value in raw.items() if key} def build_env_settings_context() -> dict[str, Any]: """Данные для формы редактирования безопасных ключей .env.""" env_values = _dotenv_values() sources = parser_sources.list_parser_sources() source_options = [ {"value": source["key"], "label": source.get("title") or source["key"]} for source in sources ] groups: list[dict[str, Any]] = [ { "title": "Общие настройки источников", "subtitle": "Эти значения влияют на выбор активного турнира по умолчанию и базовый сайт RFS.", "fields": [ _field( "DATA_EVENT_CODE", "Активный источник по умолчанию", parser_sources.get_default_source_key(), field_type="select", options=source_options, help_text="Используется, если парсер запускается без выбора источника.", ), _field( "RFS_BASE_URL", "Базовый URL RFS/WFL", parser_sources.RFS_BASE_URL, help_text="Обычно https://wfl.rfs.ru", wide=True, ), ], } ] for source in sources: source_key = source["key"] title = source.get("title") or source_key groups.append( { "title": title, "subtitle": f"Ключ источника: {source_key}", "fields": [ _field(_source_field_key(source_key, "EVENT_NAME"), "Название в интерфейсе", source.get("title", "")), _field(_source_field_key(source_key, "SEASON"), "Сезон", source.get("season", "")), _field(_source_field_key(source_key, "TOURNAMENT_ID"), "Tournament ID", source.get("tournament_id", "")), _field(_source_field_key(source_key, "ROUND_ID"), "Round ID", source.get("round_id", "")), _field( _source_field_key(source_key, "CALENDAR_TYPE"), "Тип календаря", source.get("calendar_type", "tours"), field_type="select", options=[ {"value": "tours", "label": "tours — туры"}, {"value": "stages", "label": "stages — стадии"}, ], ), _field(_source_field_key(source_key, "LOGO_BASE_PATH"), "Папка логотипов", source.get("logo_base_path", ""), wide=True), _field(_source_field_key(source_key, "TEAMS_URL"), "Ссылка на команды", source.get("teams_url", ""), wide=True), _field(_source_field_key(source_key, "SCHEDULE_URL"), "Ссылка на расписание", source.get("schedule_url", ""), wide=True), _field(_source_field_key(source_key, "STANDINGS_URL"), "Ссылка на турнирку", source.get("standings_url", ""), wide=True), _field(_source_field_key(source_key, "MATCH_BASE_URL"), "Базовая ссылка матча", source.get("match_base_url", ""), wide=True), ], } ) editable_keys = [field["key"] for group in groups for field in group["fields"]] return { "env_path": str(ENV_PATH), "groups": groups, "editable_keys": editable_keys, "raw_values": env_values, } def get_editable_env_keys() -> set[str]: context = build_env_settings_context() return set(context["editable_keys"]) def save_env_settings(form_values: dict[str, str]) -> list[str]: """Сохраняет только разрешённые ключи в .env и обновляет os.environ.""" editable_keys = get_editable_env_keys() updates: list[str] = [] ENV_PATH.parent.mkdir(parents=True, exist_ok=True) if not ENV_PATH.exists(): ENV_PATH.write_text("", encoding="utf-8") backup_path = ENV_PATH.with_suffix(".env.bak") try: shutil.copy2(ENV_PATH, backup_path) except Exception: # Бэкап полезен, но не должен ломать сохранение настроек. pass for key in sorted(editable_keys): if key not in form_values: continue value = str(form_values.get(key) or "").strip() set_key(str(ENV_PATH), key, value, quote_mode="always") os.environ[key] = value updates.append(key) load_dotenv(ENV_PATH, override=True) return updates