Добавлена проверка перед скачиванием, что файл бэкапа уже полностью

создан
This commit is contained in:
VolandSZ
2026-03-15 19:34:44 +03:00
parent d06615b0b3
commit d735cc1932
2 changed files with 64 additions and 44 deletions

108
main.py
View File

@ -3,7 +3,7 @@ import re
import time import time
# from datetime import datetime # from datetime import datetime
from dataclasses import dataclass, field from dataclasses import dataclass
# from pathlib import Path # from pathlib import Path
from playwright.sync_api import ( from playwright.sync_api import (
@ -234,7 +234,7 @@ class BackupManager:
backup_data: bool = False, backup_data: bool = False,
backup_mysql: bool = True, backup_mysql: bool = True,
wait_for_completion: bool = True, wait_for_completion: bool = True,
poll_interval: int = 30, poll_interval: int = 60,
max_wait: int = 600, max_wait: int = 600,
): ):
""" """
@ -323,7 +323,7 @@ class BackupManager:
) -> bool: ) -> bool:
"""Ожидание завершения создания бэкапа""" """Ожидание завершения создания бэкапа"""
print(f"\n[*] Ожидание завершения (макс. {max_wait} сек)...") print(f"\n[*] Ожидание завершения (макс. {max_wait} сек)...")
print(f" Ожидаем {expected_new} новых файлов") print(f" Ожидаем {expected_new} новых готовых файлов")
start_time = time.time() start_time = time.time()
attempt = 0 attempt = 0
@ -340,52 +340,73 @@ class BackupManager:
# Обновляем список через ссылку на странице # Обновляем список через ссылку на странице
self._refresh_backup_list() self._refresh_backup_list()
files_now = self.parse_backup_table() files_now = self.parse_backup_table()
count_now = len(files_now)
new_count = count_now - count_before
print(f"файлов: {count_now} (новых: {new_count})") # Считаем только ГОТОВЫЕ файлы (размер вида "12345 KB" без "формируется")
ready_files = [f for f in files_now if self._is_file_ready(f)]
ready_count = len(ready_files)
new_ready = ready_count - count_before
if new_count >= expected_new: # Считаем файлы в процессе создания
print(f"\n[OK] Бэкап создан! Новых файлов: {new_count}") in_progress = [f for f in files_now if not self._is_file_ready(f)]
print(
f"готовых: {ready_count} (новых: {new_ready}), в процессе: {len(in_progress)}"
)
if in_progress:
for f in in_progress:
print(f"{f.name}{f.size}")
if new_ready >= expected_new:
print(f"\n[OK] Бэкап создан! Новых готовых файлов: {new_ready}")
return True return True
# Проверяем статус задания # Проверяем статус задания
status = self._check_task_status() # status = self._check_task_status()
if status: # if status:
for s in status: # for s in status:
print(f" Задание: {s}") # print(f" Задание: {s}")
print(f" Следующая проверка через {poll_interval} сек...") print(f" Следующая проверка через {poll_interval} сек...")
time.sleep(poll_interval) time.sleep(poll_interval)
def _check_task_status(self) -> list[str]: def _is_file_ready(self, backup_file: BackupFile) -> bool:
"""Проверяет статус заданий в таблице Schedule/Operation""" """Проверяет что файл готов (не в процессе создания)"""
statuses = self.page.evaluate("""() => { size = backup_file.size.lower()
const tables = document.querySelectorAll('table'); # Готовый файл: "1033513 KB"
const results = []; # В процессе: "файл формируется, 50048 KB"
if "формируется" in size or "создаётся" in size or "progress" in size:
return False
return True
for (const table of tables) { # def _check_task_status(self) -> list[str]:
const headers = table.querySelectorAll('th'); # """Проверяет статус заданий в таблице Schedule/Operation"""
const headerTexts = Array.from(headers).map(h => h.innerText.trim()); # statuses = self.page.evaluate("""() => {
# const tables = document.querySelectorAll('table');
# const results = [];
if (headerTexts.some(h => h.includes('Schedule') || h.includes('Operation'))) { # for (const table of tables) {
const rows = table.querySelectorAll('tr'); # const headers = table.querySelectorAll('th');
for (const row of rows) { # const headerTexts = Array.from(headers).map(h => h.innerText.trim());
const cells = row.querySelectorAll('td');
if (cells.length >= 3) { # if (headerTexts.some(h => h.includes('Schedule') || h.includes('Operation'))) {
const schedule = cells[0]?.innerText?.trim() || ''; # const rows = table.querySelectorAll('tr');
const server = cells[1]?.innerText?.trim() || ''; # for (const row of rows) {
const operation = cells[2]?.innerText?.trim() || ''; # const cells = row.querySelectorAll('td');
if (schedule || operation) { # if (cells.length >= 3) {
results.push(`${schedule} | ${server} | ${operation}`); # const schedule = cells[0]?.innerText?.trim() || '';
} # const server = cells[1]?.innerText?.trim() || '';
} # const operation = cells[2]?.innerText?.trim() || '';
} # if (schedule || operation) {
} # results.push(`${schedule} | ${server} | ${operation}`);
} # }
return results; # }
}""") # }
return statuses # }
# }
# return results;
# }""")
# return statuses
def create_backup_mysql_only(self, wait: bool = True): def create_backup_mysql_only(self, wait: bool = True):
"""Создать только бэкап базы данных""" """Создать только бэкап базы данных"""
@ -647,16 +668,15 @@ class BackupManager:
# print("ПРОПУЩЕНО (удаление закомментировано)") # print("ПРОПУЩЕНО (удаление закомментировано)")
print(f"\n[OK] Готово") print("\n[OK] Готово")
# === ИСПОЛЬЗОВАНИЕ === # === ИСПОЛЬЗОВАНИЕ ===
if __name__ == "__main__": if __name__ == "__main__":
mgr = BackupManager( mgr = BackupManager(
username="minzdravmo", username="rav",
password="KB3ZS7*sv6j4zYr@bPdeFWQN38rbtQv", password="KB3e8rbtQv",
# backup_folder=Path("F:/test_folder/MyReports"),
account_id="564622", account_id="564622",
) )
@ -678,9 +698,9 @@ if __name__ == "__main__":
) )
# 3. Показать, что удаляем и удалить # 3. Показать, что удаляем и удалить
mgr.print_deletion_plan(delete_count=1, year=2026) # mgr.print_deletion_plan(delete_count=1, year=2026)
mgr.delete_old_backups(delete_count=1, year=2026) # mgr.delete_old_backups(delete_count=1, year=2026)
# Скачать только последний бэкап (без создания нового) # Скачать только последний бэкап (без создания нового)
# mgr.download_latest(save_dir="./backups") # mgr.download_latest(save_dir="./backups")

BIN
requirements.txt Normal file

Binary file not shown.