diff --git a/Backup/BackupExterniDB01.py b/Backup/BackupExterniDB01.py new file mode 100644 index 0000000..c4e1789 --- /dev/null +++ b/Backup/BackupExterniDB01.py @@ -0,0 +1,172 @@ +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"c:\medicusext") +BACKUP_DIR = Path(r"U:\medicusbackup") + +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() diff --git a/Backup/BackupExterniDB02.py b/Backup/BackupExterniDB02.py new file mode 100644 index 0000000..1552217 --- /dev/null +++ b/Backup/BackupExterniDB02.py @@ -0,0 +1,189 @@ +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"c:\medicusext") +BACKUP_DIR = Path(r"U:\medicusbackup") + +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 = [] + fbk_paths = [] # FBK files to be zipped together + + # -------------------------------------------------------- + # 1) GBAK all databases + # -------------------------------------------------------- + for fdb in fdb_all: + name = fdb.stem + fbk = BACKUP_DIR / f"{name}_{ts}.fbk" + 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: + 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)") + + # Delete log, keep FBK for zipping + log.unlink() + + result["ok"] = True + fbk_paths.append((fbk, result)) + backed_up.append(result) + + except Exception: + result["error"] = traceback.format_exc() + errors.append(result) + for f in (fbk, log): + if f.exists(): + f.unlink() + + # -------------------------------------------------------- + # 2) ZIP all FBK files into one archive + # -------------------------------------------------------- + total_zip_size = 0 + if fbk_paths: + zip_path = BACKUP_DIR / f"MEDICUS_FILES_{ts}.zip" + print(f"\nZIP: {zip_path.name}") + t_zip_start = time.time() + + # Calculate total size for progress + total_fbk_size = sum(fbk.stat().st_size for fbk, _ in fbk_paths) + total_processed = 0 + + with zipfile.ZipFile( + zip_path, "w", + compression=zipfile.ZIP_DEFLATED, + compresslevel=9, + ) as zf: + for fbk, result in fbk_paths: + 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) + total_processed += len(buf) + pct = total_processed * 100 / total_fbk_size + print( + f"\r {fbk.name}: {pct:6.2f}%", + end="", flush=True, + ) + print() + + t_zip_total = time.time() - t_zip_start + total_zip_size = zip_path.stat().st_size + print(f"ZIP OK ({t_zip_total:.0f}s, {total_zip_size/1024/1024:.1f} MB)") + + # Fill zip_size into each result and delete FBK files + for fbk, result in fbk_paths: + result["zip_size"] = total_zip_size + fbk.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_fbk_mb = sum(r["fbk_size"] for r in backed_up) / 1024 / 1024 + for r in backed_up: + report.append( + f" {r['file']}: " + f"FBK {r['fbk_size']/1024/1024:.1f} MB " + f"(gbak {r['t_gbak']:.0f}s)" + ) + report.append(f" Total FBK: {total_fbk_mb:.1f} MB -> ZIP: {total_zip_size/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()