Действия вынесены в параметры запуска

This commit is contained in:
VolandSZ
2026-03-28 18:51:56 +03:00
parent 1d05d3881e
commit 2a66f51b1c

243
main.py
View File

@ -1,4 +1,5 @@
import os
import argparse
import os
import re
import sys
import time
@ -132,10 +133,24 @@ class BackupManager:
self.page.goto(self.backup_url, wait_until="networkidle")
self.page.wait_for_timeout(10000)
def _refresh_backup_list(self):
"""Обновить список бэкапов кликом по ссылке 'обновить список / статусы'"""
self.page.click("a:has-text('обновить список / статусы')")
self.page.wait_for_timeout(10000)
def _extract_timestamp(self, name: str) -> str:
match = re.search(r"(\d{4}_\d{2}_\d{2}_\d{2}_\d{2}_\d{2})", name)
return match.group(1) if match else ""
def _is_file_ready(self, backup_file: BackupFile) -> bool:
"""Проверяет что файл готов (не в процессе создания)"""
size = backup_file.size.lower()
# Готовый файл: "1033513 KB"
# В процессе: "файл формируется, 50048 KB"
if "формируется" in size or "создаётся" in size or "progress" in size:
return False
return True
def parse_backup_table(self) -> list[BackupFile]:
rows_data = self.page.evaluate("""() => {
const table = document.querySelector('table.gb-ttc-2');
@ -261,6 +276,15 @@ class BackupManager:
# СОЗДАНИЕ БЭКАПОВ
# =====================================================
def _expected_new_files(self, backup_data: bool, backup_mysql: bool) -> int:
"""Сколько новых файлов ожидаем"""
count = 0
if backup_data:
count += 1 # ftp_...
if backup_mysql:
count += 2 # sqldump_... (raw + zip)
return count
def create_backup(
self,
backup_data: bool = False,
@ -286,9 +310,11 @@ class BackupManager:
self._go_to_backups()
# Запоминаем количество бэкапов до создания
files_before = self.parse_backup_table()
count_before = len(files_before)
print(f"[*] Бэкапов до создания: {count_before}")
ready_files_before = [
f for f in self.parse_backup_table() if self._is_file_ready(f)
]
count_before = len(ready_files_before)
print(f"[*] Готовых бэкапов до создания: {count_before}")
types = []
if backup_data:
@ -336,20 +362,6 @@ class BackupManager:
max_wait=max_wait,
)
def _expected_new_files(self, backup_data: bool, backup_mysql: bool) -> int:
"""Сколько новых файлов ожидаем"""
count = 0
if backup_data:
count += 1 # ftp_...
if backup_mysql:
count += 2 # sqldump_... (raw + zip)
return count
def _refresh_backup_list(self):
"""Обновить список бэкапов кликом по ссылке 'обновить список / статусы'"""
self.page.click("a:has-text('обновить список / статусы')")
self.page.wait_for_timeout(10000)
def _wait_for_backup_completion(
self, count_before: int, expected_new: int, poll_interval: int, max_wait: int
) -> bool:
@ -402,44 +414,6 @@ class BackupManager:
print(f" Следующая проверка через {poll_interval} сек...")
time.sleep(poll_interval)
def _is_file_ready(self, backup_file: BackupFile) -> bool:
"""Проверяет что файл готов (не в процессе создания)"""
size = backup_file.size.lower()
# Готовый файл: "1033513 KB"
# В процессе: "файл формируется, 50048 KB"
if "формируется" in size or "создаётся" in size or "progress" in size:
return False
return True
# def _check_task_status(self) -> list[str]:
# """Проверяет статус заданий в таблице Schedule/Operation"""
# statuses = self.page.evaluate("""() => {
# const tables = document.querySelectorAll('table');
# const results = [];
# for (const table of tables) {
# const headers = table.querySelectorAll('th');
# const headerTexts = Array.from(headers).map(h => h.innerText.trim());
# if (headerTexts.some(h => h.includes('Schedule') || h.includes('Operation'))) {
# const rows = table.querySelectorAll('tr');
# for (const row of rows) {
# const cells = row.querySelectorAll('td');
# if (cells.length >= 3) {
# 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
def create_backup_mysql_only(self, wait: bool = True):
"""Создать только бэкап базы данных"""
return self.create_backup(
@ -701,36 +675,144 @@ class BackupManager:
print("\n[OK] Готово")
def parse_args():
parser = argparse.ArgumentParser(
description="Менеджер резервных копий 1gb.ru",
formatter_class=argparse.RawTextHelpFormatter,
)
parser.add_argument(
"--action",
choices=["create", "list", "download", "delete", "create-download"],
default="list",
help="""Действие:
list — показать список бэкапов (по умолчанию)
create — создать бэкап
download — скачать последний бэкап
create-download — создать и скачать
delete — удалить старые бэкапы""",
)
parser.add_argument(
"--type",
choices=["all", "data", "db"],
default="all",
help="""Тип бэкапа (для create/create-download/download):
db — только база данных MySQL (по умолчанию)
data — только данные сайта FTP
all — данные + база""",
)
parser.add_argument(
"--keep",
type=int,
default=3,
help="Сколько бэкапов ОСТАВИТЬ при удалении (по умолчанию: 3)",
)
parser.add_argument(
"--year",
type=int,
default=2026,
help="За какой год удалять бэкапы (по умолчанию: 2026)",
)
parser.add_argument(
"--save-dir",
type=str,
default="F:/test_folder/MyReports",
help="Папка для скачивания (по умолчанию: F:/test_folder/MyReports)",
)
parser.add_argument(
"--no-wait", action="store_true", help="Не ждать завершения создания бэкапа"
)
parser.add_argument("--username", type=str, default="minzdravmo", help="Логин")
parser.add_argument(
"--password", type=str, default="KB3ZS7*sv6j4zYr@bPdeFWQN38rbtQv", help="Пароль"
)
parser.add_argument("--account", type=str, default="564622", help="ID аккаунта")
return parser.parse_args()
# === ИСПОЛЬЗОВАНИЕ ===
if __name__ == "__main__":
args = parse_args()
backup_data = args.type in ("all", "data")
backup_mysql = args.type in ("all", "db")
mgr = BackupManager(
username="minzdravmo",
password="KB3ZS7*sv6j4zYr@bPdeFWQN38rbtQv",
account_id="564622",
username=args.username, password=args.password, account_id=args.account
)
mgr.enable_output_log("F:/test_folder/MyReports/backup.log")
try:
mgr.start()
if args.action == "list":
mgr.print_backups()
elif args.action == "create":
mgr.print_backups()
print(f"\n{'=' * 70}")
mgr.create_backup(
backup_data=backup_data,
backup_mysql=backup_mysql,
wait_for_completion=not args.no_wait,
)
mgr.print_backups()
elif args.action == "download":
mgr.download_latest(
save_dir=args.save_dir,
include_data=backup_data,
include_sql_raw=backup_mysql,
include_sql_zip=backup_mysql,
)
elif args.action == "create-download":
mgr.create_and_download(
save_dir=args.save_dir,
backup_data=backup_data,
backup_mysql=backup_mysql,
)
elif args.action == "delete":
groups = mgr.get_grouped_backups()
year_groups = [g for g in groups if g.timestamp.startswith(str(args.year))]
total = len(year_groups)
if total <= args.keep:
print(
f"[*] Бэкапов за {args.year}: {total}, оставляем: {args.keep}. Удалять нечего."
)
else:
delete_count = total - args.keep
mgr.delete_old_backups(delete_count=delete_count, year=args.year)
# 1. Показать текущие бэкапы
groups = mgr.print_backups()
print("\n" + "=" * 70)
# groups = mgr.print_backups()
# print("\n" + "=" * 70)
# 2. Создать бэкап только базы данных
# mgr.create_backup_mysql_only(wait=True)
# 2. Создать бэкап и сразу скачать
mgr.create_and_download(
save_dir="F:/test_folder/MyReports",
backup_data=True, # данные FTP (закомментировано)
backup_mysql=True, # база данных
)
# mgr.create_and_download(
# save_dir="F:/test_folder/MyReports",
# backup_data=True, # данные FTP (закомментировано)
# backup_mysql=True, # база данных
# )
# 3. Показать, что удаляем и удалить
mgr.print_deletion_plan(delete_count=1, year=2026)
mgr.delete_old_backups(delete_count=1, year=2026)
# mgr.print_deletion_plan(delete_count=1, year=2026)
# mgr.delete_old_backups(delete_count=1, year=2026)
# Скачать только последний бэкап (без создания нового)
# mgr.download_latest(save_dir="./backups")
@ -751,3 +833,28 @@ if __name__ == "__main__":
finally:
mgr.stop()
# # Показать список бэкапов
# python main.py --action list
# # Создать бэкап только БД
# python main.py --action create --type db
# # Создать бэкап всего (данные + БД)
# python main.py --action create --type all
# # Создать и сразу скачать БД
# python main.py --action create-download --type db --save-dir ./backups
# # Скачать последний бэкап
# python main.py --action download --type db
# # Удалить старые, оставить 3 за 2026 год
# python main.py --action delete --keep 3 --year 2026
# # Удалить старые, оставить 1
# python main.py --action delete --keep 1 --year 2026
# # Полный цикл: создать БД + скачать + удалить старые (оставить 3)
# python main.py --action create-download --type db && python main.py --action delete --keep 3