mapper + lint

This commit is contained in:
2025-10-06 18:49:40 +03:00
parent b550cb38ff
commit 74b7779e45
18 changed files with 4512 additions and 2250 deletions

View File

@@ -1,19 +1,15 @@
# pylint: disable=too-many-arguments,too-many-locals,too-many-statements,too-many-branches,unused-argument,invalid-name,redefined-outer-name
"""
[MODULE] Superset Dashboard Backup Script
@contract: Автоматизирует процесс резервного копирования дашбордов Superset.
"""
# <GRACE_MODULE id="backup_script" name="backup_script.py">
# @SEMANTICS: backup, superset, automation, dashboard
# @PURPOSE: Этот модуль отвечает за автоматизированное резервное копирование дашбордов Superset.
# @DEPENDS_ON: superset_tool.client -> Использует SupersetClient для взаимодействия с API.
# @DEPENDS_ON: superset_tool.utils -> Использует утилиты для логирования, работы с файлами и инициализации клиентов.
# [IMPORTS] Стандартная библиотека
# <IMPORTS>
import logging
import sys
from pathlib import Path
from dataclasses import dataclass,field
# [IMPORTS] Third-party
from requests.exceptions import RequestException
# [IMPORTS] Локальные модули
from superset_tool.client import SupersetClient
from superset_tool.exceptions import SupersetAPIError
from superset_tool.utils.logger import SupersetLogger
@@ -26,11 +22,12 @@ from superset_tool.utils.fileio import (
RetentionPolicy
)
from superset_tool.utils.init_clients import setup_clients
# </IMPORTS>
# --- Начало кода модуля ---
# [ENTITY: Dataclass('BackupConfig')]
# CONTRACT:
# PURPOSE: Хранит конфигурацию для процесса бэкапа.
# <ANCHOR id="BackupConfig" type="DataClass">
# @PURPOSE: Хранит конфигурацию для процесса бэкапа.
@dataclass
class BackupConfig:
"""Конфигурация для процесса бэкапа."""
@@ -38,18 +35,26 @@ class BackupConfig:
rotate_archive: bool = True
clean_folders: bool = True
retention_policy: RetentionPolicy = field(default_factory=RetentionPolicy)
# </ANCHOR id="BackupConfig">
# [ENTITY: Function('backup_dashboards')]
# CONTRACT:
# PURPOSE: Выполняет бэкап всех доступных дашбордов для заданного клиента и окружения, пропуская ошибки экспорта.
# PRECONDITIONS:
# - `client` должен быть инициализированным экземпляром `SupersetClient`.
# - `env_name` должен быть строкой, обозначающей окружение.
# - `backup_root` должен быть валидным путем к корневой директории бэкапа.
# POSTCONDITIONS:
# - Дашборды экспортируются и сохраняются.
# - Ошибки экспорта логируются и не приводят к остановке скрипта.
# - Возвращает `True` если все дашборды были экспортированы без критических ошибок, `False` иначе.
# <ANCHOR id="backup_dashboards" type="Function">
# @PURPOSE: Выполняет бэкап всех доступных дашбордов для заданного клиента и окружения, пропуская ошибки экспорта.
# @PRE: `client` должен быть инициализированным экземпляром `SupersetClient`.
# @PRE: `env_name` должен быть строкой, обозначающей окружение.
# @PRE: `backup_root` должен быть валидным путем к корневой директории бэкапа.
# @POST: Дашборды экспортируются и сохраняются. Ошибки экспорта логируются и не приводят к остановке скрипта.
# @PARAM: client: SupersetClient - Клиент для доступа к API Superset.
# @PARAM: env_name: str - Имя окружения (e.g., 'PROD').
# @PARAM: backup_root: Path - Корневая директория для сохранения бэкапов.
# @PARAM: logger: SupersetLogger - Инстанс логгера.
# @PARAM: config: BackupConfig - Конфигурация процесса бэкапа.
# @RETURN: bool - `True` если все дашборды были экспортированы без критических ошибок, `False` иначе.
# @RELATION: CALLS -> client.get_dashboards
# @RELATION: CALLS -> client.export_dashboard
# @RELATION: CALLS -> save_and_unpack_dashboard
# @RELATION: CALLS -> archive_exports
# @RELATION: CALLS -> consolidate_archive_folders
# @RELATION: CALLS -> remove_empty_directories
def backup_dashboards(
client: SupersetClient,
env_name: str,
@@ -57,10 +62,10 @@ def backup_dashboards(
logger: SupersetLogger,
config: BackupConfig
) -> bool:
logger.info(f"[STATE][backup_dashboards][ENTER] Starting backup for {env_name}.")
logger.info(f"[backup_dashboards][Entry] Starting backup for {env_name}.")
try:
dashboard_count, dashboard_meta = client.get_dashboards()
logger.info(f"[STATE][backup_dashboards][PROGRESS] Found {dashboard_count} dashboards to export in {env_name}.")
logger.info(f"[backup_dashboards][Progress] Found {dashboard_count} dashboards to export in {env_name}.")
if dashboard_count == 0:
return True
@@ -91,8 +96,7 @@ def backup_dashboards(
success_count += 1
except (SupersetAPIError, RequestException, IOError, OSError) as db_error:
logger.error(f"[STATE][backup_dashboards][FAILURE] Failed to export dashboard {dashboard_title} (ID: {dashboard_id}): {db_error}", exc_info=True)
# Продолжаем обработку других дашбордов
logger.error(f"[backup_dashboards][Failure] Failed to export dashboard {dashboard_title} (ID: {dashboard_id}): {db_error}", exc_info=True)
continue
if config.consolidate:
@@ -101,21 +105,22 @@ def backup_dashboards(
if config.clean_folders:
remove_empty_directories(str(backup_root / env_name), logger=logger)
logger.info(f"[backup_dashboards][CoherenceCheck:Passed] Backup logic completed.")
return success_count == dashboard_count
except (RequestException, IOError) as e:
logger.critical(f"[STATE][backup_dashboards][FAILURE] Fatal error during backup for {env_name}: {e}", exc_info=True)
logger.critical(f"[backup_dashboards][Failure] Fatal error during backup for {env_name}: {e}", exc_info=True)
return False
# END_FUNCTION_backup_dashboards
# </ANCHOR id="backup_dashboards">
# [ENTITY: Function('main')]
# CONTRACT:
# PURPOSE: Основная точка входа скрипта.
# PRECONDITIONS: None
# POSTCONDITIONS: Возвращает код выхода.
# <ANCHOR id="main" type="Function">
# @PURPOSE: Основная точка входа для запуска процесса резервного копирования.
# @RETURN: int - Код выхода (0 - успех, 1 - ошибка).
# @RELATION: CALLS -> setup_clients
# @RELATION: CALLS -> backup_dashboards
def main() -> int:
log_dir = Path("P:\\Superset\\010 Бекапы\\Logs")
logger = SupersetLogger(log_dir=log_dir, level=logging.INFO, console=True)
logger.info("[STATE][main][ENTER] Starting Superset backup process.")
logger.info("[main][Entry] Starting Superset backup process.")
exit_code = 0
try:
@@ -137,20 +142,23 @@ def main() -> int:
config=backup_config
)
except Exception as env_error:
logger.critical(f"[STATE][main][FAILURE] Critical error for environment {env}: {env_error}", exc_info=True)
# Продолжаем обработку других окружений
logger.critical(f"[main][Failure] Critical error for environment {env}: {env_error}", exc_info=True)
results[env] = False
if not all(results.values()):
exit_code = 1
except (RequestException, IOError) as e:
logger.critical(f"[STATE][main][FAILURE] Fatal error in main execution: {e}", exc_info=True)
logger.critical(f"[main][Failure] Fatal error in main execution: {e}", exc_info=True)
exit_code = 1
logger.info("[STATE][main][SUCCESS] Superset backup process finished.")
logger.info("[main][Exit] Superset backup process finished.")
return exit_code
# END_FUNCTION_main
# </ANCHOR id="main">
if __name__ == "__main__":
sys.exit(main())
# --- Конец кода модуля ---
# </GRACE_MODULE id="backup_script">