Небольшие изменения
This commit is contained in:
@@ -3,6 +3,7 @@ from typing import Optional, List, Dict, Any
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
import pandas as pd
|
||||
from app.models.replication import ReplicationSchedule
|
||||
from app.services.replication_state import replication_state
|
||||
from app.services.data_reader import data_reader
|
||||
from app.services.data_writer import data_writer
|
||||
@@ -88,13 +89,12 @@ class DatabaseMigrator:
|
||||
parsed = self._parse_table_name(table_name)
|
||||
return f"{parsed['basename']}ID"
|
||||
|
||||
def migrate_table_by_time(self, table_name: str, last_sync_time: datetime) -> Dict[str, int]:
|
||||
def migrate_table_by_time(self, table_name: str, life_table_name: str, last_sync_time: datetime) -> Dict[str, int]:
|
||||
"""Миграция таблицы через Life-механизм по времени"""
|
||||
life_table = self._get_life_table_name(table_name)
|
||||
base_id_field = self._get_base_id_field(table_name)
|
||||
life_id_field = self._get_life_id_field(table_name)
|
||||
|
||||
migration_logger.info(f"Миграция {table_name} через {life_table} с {last_sync_time}")
|
||||
migration_logger.info(f"Миграция {table_name} через {life_table_name} с {last_sync_time}")
|
||||
|
||||
stats = {'inserted': 0, 'updated': 0, 'deleted': 0, 'total': 0}
|
||||
|
||||
@@ -105,12 +105,12 @@ class DatabaseMigrator:
|
||||
SELECT
|
||||
{base_id_field},
|
||||
MAX({life_id_field}) as MaxLifeID
|
||||
FROM {life_table}
|
||||
FROM {life_table_name}
|
||||
WHERE x_DateTime > CAST(? AS datetime)
|
||||
GROUP BY {base_id_field}
|
||||
)
|
||||
SELECT dl.*
|
||||
FROM {life_table} dl
|
||||
FROM {life_table_name} dl
|
||||
INNER JOIN LatestLife ll
|
||||
ON dl.{life_id_field} = ll.MaxLifeID
|
||||
"""
|
||||
@@ -211,28 +211,24 @@ class DatabaseMigrator:
|
||||
|
||||
return result
|
||||
|
||||
def migrate_table(self, table_name: str, full_reload: bool = False) -> bool:
|
||||
def migrate_table(self, table_name: str, schedule_id: int, metadata_id: int, life_table_name: Optional[str], uses_life: bool = False, full_reload: bool = False) -> bool:
|
||||
"""Миграция одной таблицы (поддерживает и ID, и Life)"""
|
||||
migration_logger.table_start(table_name)
|
||||
self.current_table = table_name
|
||||
table_start_time = datetime.now()
|
||||
|
||||
try:
|
||||
# Получаем ID колонку для статистики
|
||||
id_column = get_primary_key(table_name)
|
||||
|
||||
# Определяем, использует ли таблица Life-механизм
|
||||
uses_life = table_name in self.life_tables
|
||||
|
||||
if uses_life and not full_reload:
|
||||
if uses_life and not full_reload and life_table_name:
|
||||
# МИГРАЦИЯ ЧЕРЕЗ LIFE-ТАБЛИЦУ ПО ВРЕМЕНИ
|
||||
last_sync = self.state.get_table_last_sync(table_name)
|
||||
last_sync = self.state.get_table_last_sync(metadata_id)
|
||||
|
||||
if last_sync:
|
||||
stats = self.migrate_table_by_time(table_name, last_sync)
|
||||
stats = self.migrate_table_by_time(table_name, life_table_name, last_sync)
|
||||
|
||||
# Обновляем время синхронизации
|
||||
self.state.update_table_sync_time(table_name)
|
||||
self.state.update_table_sync_time(schedule_id)
|
||||
|
||||
# Обновляем статистику
|
||||
if id_column:
|
||||
@@ -340,7 +336,7 @@ class DatabaseMigrator:
|
||||
})
|
||||
return False
|
||||
|
||||
def _incremental_by_id(self, table_name: str) -> bool:
|
||||
def _incremental_by_id(self, table_name: str, metadata) -> bool:
|
||||
"""Инкрементальная загрузка по ID (для таблиц без Life)"""
|
||||
migration_logger.info(f"Инкрементальная загрузка {table_name} по ID")
|
||||
|
||||
@@ -419,11 +415,11 @@ class DatabaseMigrator:
|
||||
def create_all_foreign_keys(self):
|
||||
"""Создать все внешние ключи после завершения миграции"""
|
||||
if not self.all_foreign_keys:
|
||||
migration_logger.info("ℹ️ Нет внешних ключей для создания")
|
||||
migration_logger.info("Нет внешних ключей для создания")
|
||||
return
|
||||
|
||||
migration_logger.info("="*60)
|
||||
migration_logger.info("🔗 СОЗДАНИЕ ВНЕШНИХ КЛЮЧЕЙ")
|
||||
migration_logger.info("СОЗДАНИЕ ВНЕШНИХ КЛЮЧЕЙ")
|
||||
migration_logger.info("="*60)
|
||||
|
||||
for table_name, foreign_keys in self.all_foreign_keys.items():
|
||||
@@ -448,16 +444,17 @@ class DatabaseMigrator:
|
||||
'time': datetime.now()
|
||||
})
|
||||
|
||||
def run_migration(self, tables: Optional[List[str]] = None, full_reload: bool = False, send_email: bool = True):
|
||||
"""Запуск миграции для всех таблиц"""
|
||||
def run_migration(
|
||||
self, table_name: str, schedule_id: int, metadata_id: int,
|
||||
life_table_name: Optional[str] = None, uses_life: bool = False,
|
||||
full_reload: bool = False, send_email: bool = True
|
||||
):
|
||||
"""Запуск миграции таблицы"""
|
||||
self.is_running = True
|
||||
self.start_time = datetime.now()
|
||||
self.all_foreign_keys = {}
|
||||
self.errors = []
|
||||
|
||||
if tables is None:
|
||||
tables = settings.TABLES_TO_COPY
|
||||
|
||||
|
||||
last_replication = self.state.get_last_replication_time()
|
||||
|
||||
migration_logger.info("="*70)
|
||||
@@ -465,19 +462,16 @@ class DatabaseMigrator:
|
||||
migration_logger.info(f"Время старта: {self.start_time}")
|
||||
if last_replication:
|
||||
migration_logger.info(f"Последняя миграция: {last_replication}")
|
||||
migration_logger.info(f"Таблиц для обработки: {len(tables)}")
|
||||
migration_logger.info(f"Таблица для обработки: {table_name}")
|
||||
migration_logger.info(f"Режим: {'ПОЛНАЯ' if full_reload else 'ИНКРЕМЕНТАЛЬНАЯ'}")
|
||||
migration_logger.info(f"Таблицы с Life-механизмом: {self.life_tables}")
|
||||
migration_logger.info("="*70)
|
||||
|
||||
results = {}
|
||||
for i, table_name in enumerate(tables, 1):
|
||||
if not self.is_running:
|
||||
migration_logger.warning("Миграция остановлена пользователем")
|
||||
break
|
||||
if not self.is_running:
|
||||
migration_logger.warning("Миграция остановлена пользователем")
|
||||
return
|
||||
|
||||
migration_logger.info(f"\n[{i}/{len(tables)}] Обработка таблицы {table_name}")
|
||||
results[table_name] = self.migrate_table(table_name, full_reload)
|
||||
migration_logger.info(f"Обработка таблицы {table_name}")
|
||||
results = self.migrate_table(table_name, schedule_id, metadata_id, life_table_name, uses_life, full_reload)
|
||||
|
||||
# Создаем внешние ключи после всех таблиц
|
||||
self.create_all_foreign_keys()
|
||||
@@ -494,21 +488,20 @@ class DatabaseMigrator:
|
||||
self.is_running = False
|
||||
return results
|
||||
|
||||
def _log_final_stats(self, results: dict, stats: dict, total_time: float):
|
||||
def _log_final_stats(self, has_migrated: bool, stats: dict, total_time: float):
|
||||
"""Логирует финальную статистику"""
|
||||
migration_logger.info("="*70)
|
||||
migration_logger.info("ИТОГОВАЯ СТАТИСТИКА")
|
||||
migration_logger.info("="*70)
|
||||
migration_logger.info(f"Успешно: {sum(1 for r in results.values() if r)}/{len(results)}")
|
||||
migration_logger.info(f"Ошибок: {len(self.errors)}")
|
||||
migration_logger.info(f"Всего строк в БД: {stats.get('total_rows', 0)}")
|
||||
migration_logger.info(f"Общее время: {total_time:.1f}с")
|
||||
migration_logger.info("="*70)
|
||||
|
||||
def _send_notification(self, results: dict, stats: dict, total_time: float):
|
||||
def _send_notification(self, has_migrated: bool, stats: dict, total_time: float):
|
||||
"""Отправляет уведомление о результате"""
|
||||
if self.errors:
|
||||
error_body = self._build_error_email_body(results, stats, total_time)
|
||||
error_body = self._build_error_email_body(has_migrated, stats, total_time)
|
||||
email_sender.send_email(
|
||||
subject=f"МИГРАЦИЯ С ОШИБКАМИ - {datetime.now().strftime('%Y-%m-%d %H:%M')}",
|
||||
body=error_body
|
||||
@@ -516,10 +509,10 @@ class DatabaseMigrator:
|
||||
# else:
|
||||
# email_sender.send_success_notification(stats, total_time)
|
||||
|
||||
def _build_error_email_body(self, results: dict, stats: dict, total_time: float) -> str:
|
||||
def _build_error_email_body(self, has_migrated: bool, stats: dict, total_time: float) -> str:
|
||||
"""Строит тело письма с ошибками"""
|
||||
body = f"""
|
||||
🚨 МИГРАЦИЯ ЗАВЕРШЕНА С ОШИБКАМИ
|
||||
МИГРАЦИЯ ЗАВЕРШЕНА С ОШИБКАМИ
|
||||
{'='*60}
|
||||
|
||||
Время: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
||||
@@ -527,7 +520,6 @@ class DatabaseMigrator:
|
||||
|
||||
СТАТИСТИКА:
|
||||
{'='*40}
|
||||
Успешно: {sum(1 for r in results.values() if r)}/{len(results)}
|
||||
Ошибок: {len(self.errors)}
|
||||
Всего строк: {stats.get('total_rows', 0)}
|
||||
|
||||
@@ -584,7 +576,7 @@ class DatabaseMigrator:
|
||||
# Поэтому используем отдельный метод для установки
|
||||
replication_state._set_table_total_rows(table_name, dst_stats['total_rows'])
|
||||
|
||||
migration_logger.info(f" Статистика обновлена: {dst_stats['total_rows']} строк, max_id={dst_stats['max_id']}")
|
||||
migration_logger.info(f"Статистика обновлена: {dst_stats['total_rows']} строк, max_id={dst_stats['max_id']}")
|
||||
|
||||
# Логируем операцию
|
||||
self.state.log_operation(
|
||||
|
||||
Reference in New Issue
Block a user