Files
medicus/Backup/BackupExterniDB.py
Vladimir Buzalka 5674de1247 Add backup script for external Firebird databases
Nightly backup of all MEDICUS_FILES_*.fdb from U:\externi to U:\externabackup via gbak + zip with email notification.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 07:28:44 +01:00

173 lines
4.9 KiB
Python

import subprocess
import json
import os
from pathlib import Path
from datetime import datetime
import zipfile
import time
import traceback
from EmailMessagingGraph import send_mail
# ============================================================
# CONFIG
# ============================================================
GBAK = r"C:\Program Files\Firebird\Firebird_2_5_CGM\bin\gbak.exe"
FB_USER = "SYSDBA"
FB_PASS = "masterkey"
FB_PORT = "3050"
SRC_DIR = Path(r"U:\externi")
BACKUP_DIR = Path(r"U:\externabackup")
MAIL_TO = "vladimir.buzalka@buzalka.cz"
CHUNK = 8 * 1024 * 1024 # 8 MB
# ============================================================
# MAIN
# ============================================================
def main():
BACKUP_DIR.mkdir(parents=True, exist_ok=True)
now = datetime.now()
ts = now.strftime("%Y-%m-%d_%H-%M-%S")
# Find all FDB files (case-insensitive)
fdb_files = sorted(SRC_DIR.glob("MEDICUS_FILES_*.fdb"))
fdb_upper = sorted(SRC_DIR.glob("MEDICUS_FILES_*.FDB"))
fdb_all = sorted(
set(fdb_files + fdb_upper),
key=lambda p: p.name.lower(),
)
backed_up = []
errors = []
for fdb in fdb_all:
name = fdb.stem
fbk = BACKUP_DIR / f"{name}_{ts}.fbk"
zipf = BACKUP_DIR / f"{name}_{ts}.zip"
log = BACKUP_DIR / f"{name}_{ts}.log"
result = {
"file": fdb.name,
"ok": False,
"fbk_size": 0,
"zip_size": 0,
"t_gbak": 0,
"t_zip": 0,
"error": None,
}
try:
# 1) GBAK
print(f"GBAK: {fdb.name} ... ", end="", flush=True)
t0 = time.time()
db_conn = f"localhost/{FB_PORT}:{fdb}"
cmd = [
GBAK, "-b",
"-user", FB_USER,
"-pas", FB_PASS,
db_conn, str(fbk),
"-v",
]
with open(log, "w", encoding="utf-8") as f:
subprocess.run(
cmd, stdout=f, stderr=subprocess.STDOUT, check=True,
)
result["t_gbak"] = time.time() - t0
result["fbk_size"] = fbk.stat().st_size
print(f"OK ({result['t_gbak']:.0f}s)")
# 2) ZIP
t1 = time.time()
processed = 0
fbk_size = result["fbk_size"]
with zipfile.ZipFile(
zipf, "w",
compression=zipfile.ZIP_DEFLATED,
compresslevel=9,
) as zf:
zi = zipfile.ZipInfo(fbk.name)
zi.compress_type = zipfile.ZIP_DEFLATED
with zf.open(zi, "w", force_zip64=True) as z:
with open(fbk, "rb") as src:
while buf := src.read(CHUNK):
z.write(buf)
processed += len(buf)
pct = processed * 100 / fbk_size
print(
f"\r ZIP {name}: {pct:6.2f}%",
end="", flush=True,
)
print()
result["t_zip"] = time.time() - t1
result["zip_size"] = zipf.stat().st_size
# 3) DELETE FBK + LOG
fbk.unlink()
log.unlink()
result["ok"] = True
backed_up.append(result)
except Exception:
result["error"] = traceback.format_exc()
errors.append(result)
for f in (fbk, log):
if f.exists():
f.unlink()
# Build report
report = []
report.append(f"Backup externi DB - {now.strftime('%d.%m.%Y %H:%M')}")
report.append(f"Celkem souboru: {len(fdb_all)}")
report.append(f"Zalohovano: {len(backed_up)}")
report.append(f"Chyby: {len(errors)}")
report.append("")
if backed_up:
report.append("--- Backed up ---")
total_zip = 0
for r in backed_up:
total_zip += r["zip_size"]
report.append(
f" {r['file']}: "
f"FBK {r['fbk_size']/1024/1024:.1f} MB -> "
f"ZIP {r['zip_size']/1024/1024:.1f} MB "
f"(gbak {r['t_gbak']:.0f}s, zip {r['t_zip']:.0f}s)"
)
report.append(f" Total ZIP: {total_zip / 1024 / 1024:.1f} MB")
report.append("")
if errors:
report.append("--- ERRORS ---")
for r in errors:
report.append(f" {r['file']}: {r['error']}")
report.append("")
# Send email
has_errors = len(errors) > 0
subject = (
f"{'X' if has_errors else 'OK'} "
f"MEDICUS externi DB - "
f"backup {len(backed_up)}/{len(fdb_all)}"
f"{f', {len(errors)} errors' if has_errors else ''}"
)
send_mail(MAIL_TO, subject, "\n".join(report))
print("\n" + "\n".join(report))
if errors:
raise RuntimeError(f"{len(errors)} backup(s) failed")
if __name__ == "__main__":
main()