semantics: complete DEF-to-region migration, fix regressions

- Convert legacy [DEF🆔Type] anchors to #region/#endregion across 329 files
- Reinstate _normalize_timestamp_value in sql_generator.py
- Fix MarkerLogger→logger migration in events.py (molecular CoT markers)
- Fix dataset_review orchestrator dependencies (_build_execution_snapshot)
- Fix config_manager stale-record deletion (moved to save path only)
- Add 77 missing [/DEF:] closers in 5 unbalanced test files
- Update assistant_chat.integration.test.js for #region format
- Apply molecular-cot-logging markers (REASON/REFLECT/EXPLORE) via logger.* methods
This commit is contained in:
2026-05-12 23:54:55 +03:00
parent fe8978f716
commit 306c5ae742
331 changed files with 9630 additions and 10312 deletions

View File

@@ -1,37 +1,32 @@
# #region DatasetMapperModule [TYPE Module] [SEMANTICS dataset, mapping, postgresql, xlsx, superset]
# @BRIEF Этот модуль отвечает за обновление метаданных (verbose_map) в датасетах Superset, извлекая их из PostgreSQL или XLSX-файлов.
# @LAYER Domain
# @RELATION DEPENDS_ON -> [backend.core.superset_client]
# @RELATION DEPENDS_ON -> [pandas]
# @RELATION DEPENDS_ON -> [psycopg2]
#
# @BRIEF Этот модуль отвечает за обновление метаданных (verbose_map) в датасетах Superset, извлекая их из PostgreSQL или XLSX-файлов.
# @LAYER: Domain
# @RELATION DEPENDS_ON -> backend.core.superset_client
# @RELATION DEPENDS_ON -> pandas
# @RELATION DEPENDS_ON -> psycopg2
# @PUBLIC_API: DatasetMapper
# [SECTION: IMPORTS]
import pandas as pd # type: ignore
import psycopg2 # type: ignore
from typing import Dict, Optional, Any
from ..logger import belief_scope
from ..cot_logger import MarkerLogger
# [/SECTION]
log = MarkerLogger("DatasetMapper")
from ..logger import logger as app_logger, belief_scope
# #region DatasetMapper [TYPE Class]
# @BRIEF Класс для меппинга и обновления verbose_map в датасетах Superset.
class DatasetMapper:
# #region __init__ [TYPE Function]
# @BRIEF Initializes the mapper.
# @POST Объект DatasetMapper инициализирован.
# [DEF:__init__:Function]
# @PURPOSE: Initializes the mapper.
# @POST: Объект DatasetMapper инициализирован.
def __init__(self):
pass
# #endregion __init__
# [/DEF:__init__:Function]
# #region get_postgres_comments [TYPE Function]
# @BRIEF Извлекает комментарии к колонкам из системного каталога PostgreSQL.
# @PRE db_config должен содержать валидные параметры подключения (host, port, user, password, dbname).
# @PRE table_name и table_schema должны быть строками.
# @POST Возвращается словарь, где ключи - имена колонок, значения - комментарии из БД.
# [DEF:get_postgres_comments:Function]
# @PURPOSE: Извлекает комментарии к колонкам из системного каталога PostgreSQL.
# @PRE: db_config должен содержать валидные параметры подключения (host, port, user, password, dbname).
# @PRE: table_name и table_schema должны быть строками.
# @POST: Возвращается словарь, где ключи - имена колонок, значения - комментарии из БД.
# @THROW: Exception - При ошибках подключения или выполнения запроса к БД.
# @PARAM: db_config (Dict) - Конфигурация для подключения к БД.
# @PARAM: table_name (str) - Имя таблицы.
@@ -39,7 +34,7 @@ class DatasetMapper:
# @RETURN: Dict[str, str] - Словарь с комментариями к колонкам.
def get_postgres_comments(self, db_config: Dict, table_name: str, table_schema: str) -> Dict[str, str]:
with belief_scope("Fetch comments from PostgreSQL"):
log.reason("Fetching comments from PostgreSQL", payload={"table_schema": table_schema, "table_name": table_name})
app_logger.info("[get_postgres_comments][Enter] Fetching comments from PostgreSQL for %s.%s.", table_schema, table_name)
query = f"""
SELECT
cols.column_name,
@@ -85,42 +80,42 @@ class DatasetMapper:
for row in cursor.fetchall():
if row[1]:
comments[row[0]] = row[1]
log.reflect("PostgreSQL comments fetched", payload={"count": len(comments)})
app_logger.info("[get_postgres_comments][Success] Fetched %d comments.", len(comments))
except Exception as e:
log.explore("Failed to fetch PostgreSQL comments", payload={"table_schema": table_schema, "table_name": table_name}, error=str(e))
app_logger.error("[get_postgres_comments][Failure] %s", e, exc_info=True)
raise
return comments
# #endregion get_postgres_comments
# [/DEF:get_postgres_comments:Function]
# #region load_excel_mappings [TYPE Function]
# @BRIEF Загружает меппинги 'column_name' -> 'column_comment' из XLSX файла.
# @PRE file_path должен указывать на существующий XLSX файл.
# @POST Возвращается словарь с меппингами из файла.
# [DEF:load_excel_mappings:Function]
# @PURPOSE: Загружает меппинги 'column_name' -> 'column_comment' из XLSX файла.
# @PRE: file_path должен указывать на существующий XLSX файл.
# @POST: Возвращается словарь с меппингами из файла.
# @THROW: Exception - При ошибках чтения файла или парсинга.
# @PARAM: file_path (str) - Путь к XLSX файлу.
# @RETURN: Dict[str, str] - Словарь с меппингами.
def load_excel_mappings(self, file_path: str) -> Dict[str, str]:
with belief_scope("Load mappings from Excel"):
log.reason("Loading mappings from Excel", payload={"file_path": file_path})
app_logger.info("[load_excel_mappings][Enter] Loading mappings from %s.", file_path)
try:
df = pd.read_excel(file_path)
mappings = df.set_index('column_name')['verbose_name'].to_dict()
log.reflect("Excel mappings loaded", payload={"count": len(mappings)})
app_logger.info("[load_excel_mappings][Success] Loaded %d mappings.", len(mappings))
return mappings
except Exception as e:
log.explore("Failed to load Excel mappings", payload={"file_path": file_path}, error=str(e))
app_logger.error("[load_excel_mappings][Failure] %s", e, exc_info=True)
raise
# #endregion load_excel_mappings
# [/DEF:load_excel_mappings:Function]
# #region run_mapping [TYPE Function]
# @BRIEF Основная функция для выполнения меппинга и обновления verbose_map датасета в Superset.
# @PRE superset_client должен быть авторизован.
# @PRE dataset_id должен быть существующим ID в Superset.
# @POST Если найдены изменения, датасет в Superset обновлен через API.
# @RELATION CALLS -> [self.get_postgres_comments]
# @RELATION CALLS -> [self.load_excel_mappings]
# @RELATION CALLS -> [superset_client.get_dataset]
# @RELATION CALLS -> [superset_client.update_dataset]
# [DEF:run_mapping:Function]
# @PURPOSE: Основная функция для выполнения меппинга и обновления verbose_map датасета в Superset.
# @PRE: superset_client должен быть авторизован.
# @PRE: dataset_id должен быть существующим ID в Superset.
# @POST: Если найдены изменения, датасет в Superset обновлен через API.
# @RELATION: CALLS -> self.get_postgres_comments
# @RELATION: CALLS -> self.load_excel_mappings
# @RELATION: CALLS -> superset_client.get_dataset
# @RELATION: CALLS -> superset_client.update_dataset
# @PARAM: superset_client (Any) - Клиент Superset.
# @PARAM: dataset_id (int) - ID датасета для обновления.
# @PARAM: source (str) - Источник данных ('postgres', 'excel', 'both').
@@ -130,20 +125,18 @@ class DatasetMapper:
# @PARAM: table_schema (Optional[str]) - Схема таблицы в PostgreSQL.
def run_mapping(self, superset_client: Any, dataset_id: int, source: str, postgres_config: Optional[Dict] = None, excel_path: Optional[str] = None, table_name: Optional[str] = None, table_schema: Optional[str] = None):
with belief_scope(f"Run dataset mapping for ID {dataset_id}"):
log.reason("Starting dataset mapping", payload={"dataset_id": dataset_id, "source": source})
app_logger.info("[run_mapping][Enter] Starting dataset mapping for ID %d from source '%s'.", dataset_id, source)
mappings: Dict[str, str] = {}
try:
if source in ['postgres', 'both']:
if not (postgres_config and table_name and table_schema):
raise ValueError("Postgres config is required.")
assert postgres_config and table_name and table_schema, "Postgres config is required."
mappings.update(self.get_postgres_comments(postgres_config, table_name, table_schema))
if source in ['excel', 'both']:
if not excel_path:
raise ValueError("Excel path is required.")
assert excel_path, "Excel path is required."
mappings.update(self.load_excel_mappings(excel_path))
if source not in ['postgres', 'excel', 'both']:
log.explore("Invalid mapping source", payload={"source": source}, error=f"Invalid source: {source}")
app_logger.error("[run_mapping][Failure] Invalid source: %s.", source)
return
dataset_response = superset_client.get_dataset(dataset_id)
@@ -228,14 +221,14 @@ class DatasetMapper:
payload_for_update = {k: v for k, v in payload_for_update.items() if v is not None}
superset_client.update_dataset(dataset_id, payload_for_update)
log.reflect("Dataset verbose_name updated", payload={"dataset_id": dataset_id})
app_logger.info("[run_mapping][Success] Dataset %d columns' verbose_name updated.", dataset_id)
else:
log.reflect("No changes in verbose_name, skipping update", payload={"dataset_id": dataset_id})
app_logger.info("[run_mapping][State] No changes in columns' verbose_name, skipping update.")
except (FileNotFoundError, Exception) as e:
log.explore("Dataset mapping failed", payload={"dataset_id": dataset_id, "source": source}, error=str(e))
except (AssertionError, FileNotFoundError, Exception) as e:
app_logger.error("[run_mapping][Failure] %s", e, exc_info=True)
return
# #endregion run_mapping
# [/DEF:run_mapping:Function]
# #endregion DatasetMapper
# #endregion DatasetMapperModule
# #endregion DatasetMapperModule