Compare commits

...

3 Commits

Author SHA1 Message Date
administrator 52f04c2839 reporter 2026-04-28 06:11:30 +02:00
administrator 2de8711ab7 reporter 2026-04-28 06:08:43 +02:00
administrator 9dbd3ab0b4 notebook 2026-04-28 05:51:50 +02:00
5 changed files with 83 additions and 5 deletions
+5
View File
@@ -51,3 +51,8 @@ cache/
# MySQL dumps / exports # MySQL dumps / exports
# =============================== # ===============================
*.sql *.sql
# ===============================
# Claude Code
# ===============================
.claude/worktrees/
+2 -2
View File
@@ -3,8 +3,8 @@ import ctypes
from blake3 import blake3 from blake3 import blake3
# Windows atributy pro cloud/placeholder soubory # Windows atributy pro cloud/placeholder soubory
_FILE_ATTRIBUTE_OFFLINE = 0x00001000 _FILE_ATTRIBUTE_OFFLINE = 0x00001000
_FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x00040000 _FILE_ATTRIBUTE_RECALL_ON_OPEN = 0x00040000
_FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x00400000 _FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS = 0x00400000
_CLOUD_MASK = _FILE_ATTRIBUTE_OFFLINE | _FILE_ATTRIBUTE_RECALL_ON_OPEN | _FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS _CLOUD_MASK = _FILE_ATTRIBUTE_OFFLINE | _FILE_ATTRIBUTE_RECALL_ON_OPEN | _FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS
+71 -1
View File
@@ -74,6 +74,7 @@ def main():
# 5a) NEW files — compute BLAKE3, batch INSERT # 5a) NEW files — compute BLAKE3, batch INSERT
skipped_files = [] skipped_files = []
new_files = []
if new_paths: if new_paths:
print(f" Hashing {len(new_paths)} new files...") print(f" Hashing {len(new_paths)} new files...")
new_files = [] new_files = []
@@ -180,6 +181,7 @@ def main():
"modified": len(modified_paths), "modified": len(modified_paths),
"deleted": len(deleted_paths), "deleted": len(deleted_paths),
"unchanged": len(unchanged_paths), "unchanged": len(unchanged_paths),
"skipped": len(skipped_files),
} }
finalize_run(cur, run_id, stats) finalize_run(cur, run_id, stats)
conn.commit() conn.commit()
@@ -213,10 +215,14 @@ def main():
print("=" * 60) print("=" * 60)
# ── 8. Generate Excel report ── # ── 8. Generate Excel report ──
report_path = None
try: try:
from report import generate_report from report import generate_report
report_dir = r"u:\Dropbox\!!!Days\Downloads Z230" report_dir = r"z:\Dropbox\!!!Days\Downloads Z230"
for f in os.listdir(report_dir):
if f.endswith("DropboxBackupReport.xlsx"):
os.remove(os.path.join(report_dir, f))
timestamp = datetime.now().strftime("%Y-%m-%d %H_%M") timestamp = datetime.now().strftime("%Y-%m-%d %H_%M")
report_path = os.path.join(report_dir, f"{timestamp} DropboxBackupReport.xlsx") report_path = os.path.join(report_dir, f"{timestamp} DropboxBackupReport.xlsx")
print(f"\n[8] Generating report...") print(f"\n[8] Generating report...")
@@ -224,6 +230,70 @@ def main():
except Exception as e: except Exception as e:
print(f" WARN: Report generation failed: {e}") print(f" WARN: Report generation failed: {e}")
# ── 9. Send email notification ──
try:
import sys
sys.path.insert(0, r"C:\Reporting\knihovny")
from EmailMessagingGraph import send_mail
ts = datetime.now().strftime("%d.%m.%Y %H:%M")
changes = stats['new'] + stats['modified'] + stats['deleted']
report_line = f"<tr><td>Report</td><td>{report_path}</td></tr>" if report_path else ""
skipped_row = ""
skipped_detail = ""
if skipped_files:
skipped_row = f"<tr style='background:#fff3cd;color:#856404;'><td><b>Preskocene</b></td><td>{len(skipped_files):,}</td></tr>"
rows = "".join(f"<tr><td>{p}</td><td>{r}</td></tr>" for p, r in skipped_files)
skipped_detail = f"""
<h3 style="color:#856404;">&#9888; Preskocene soubory ({len(skipped_files)})</h3>
<table border="0" cellpadding="4" cellspacing="0" style="border-collapse:collapse;font-size:12px;">
<tr style="background:#f0f4fa;"><td><b>Soubor</b></td><td><b>Duvod</b></td></tr>
{rows}
</table>"""
def _file_section(title, color, paths):
if not paths:
return ""
rows = "".join(f"<tr><td style='padding:2px 8px;font-size:12px;'>{p}</td></tr>" for p in sorted(paths))
return f"""
<h3 style="color:{color};margin-top:18px;">{title} ({len(paths)})</h3>
<table border="0" cellpadding="2" cellspacing="0" style="border-collapse:collapse;width:100%;font-family:monospace;">
{rows}
</table>"""
new_paths_ok = [nf["relative_path"] for nf in new_files]
files_detail = (
_file_section("&#10003; Nove soubory", "#2a7a2a", new_paths_ok)
+ _file_section("&#9998; Zmenene soubory", "#a07000", list(modified_paths))
+ _file_section("&#10007; Smazane soubory", "#a00000", list(deleted_paths))
)
body = f"""
<html><body style="font-family:Segoe UI,Arial,sans-serif;font-size:14px;color:#222;">
<h2 style="color:#2e6da4;">&#10003; Dropbox Ordinace Backup &ndash; {ts}</h2>
<table border="0" cellpadding="6" cellspacing="0" style="border-collapse:collapse;min-width:350px;">
<tr style="background:#f0f4fa;"><td><b>Run #</b></td><td>{run_id}</td></tr>
<tr><td><b>Celkem souboru</b></td><td>{stats['total']:,}</td></tr>
<tr style="background:#f0f4fa;color:#2a7a2a;"><td><b>Nove</b></td><td>{stats['new']:,}</td></tr>
<tr style="color:#a07000;"><td><b>Zmenene</b></td><td>{stats['modified']:,}</td></tr>
<tr style="background:#f0f4fa;color:#a00000;"><td><b>Smazane</b></td><td>{stats['deleted']:,}</td></tr>
<tr><td><b>Nezmenene</b></td><td>{stats['unchanged']:,}</td></tr>
<tr style="background:#f0f4fa;"><td><b>Zmen celkem</b></td><td>{changes:,}</td></tr>
{skipped_row}
{report_line}
</table>
{files_detail}
{skipped_detail}
<p style="color:#888;font-size:12px;margin-top:20px;">REPORTER &bull; {ts}</p>
</body></html>
"""
subject = f"Dropbox Backup #{run_id} \u2013 {ts} ({changes} zmen)"
send_mail("vladimir.buzalka@buzalka.cz", subject, body, html=True)
print(f"\n[9] Email odeslan na vladimir.buzalka@buzalka.cz")
except Exception as e:
print(f" WARN: Email failed: {e}")
if __name__ == "__main__": if __name__ == "__main__":
main() main()
+2 -2
View File
@@ -77,7 +77,7 @@ def generate_report(output_path: str):
cell.alignment = Alignment(horizontal="center") cell.alignment = Alignment(horizontal="center")
cell.border = thin_border cell.border = thin_border
for row_idx, ev in enumerate(all_events, 2): for row_idx, ev in enumerate(reversed(all_events), 2):
run_id, started, event_type, rel_path, file_name, directory, old_size, new_size = ev run_id, started, event_type, rel_path, file_name, directory, old_size, new_size = ev
size_change = "" size_change = ""
@@ -115,7 +115,7 @@ def generate_report(output_path: str):
if __name__ == "__main__": if __name__ == "__main__":
REPORT_DIR = r"u:\Dropbox\!!!Days\Downloads Z230" REPORT_DIR = r"z:\Dropbox\!!!Days\Downloads Z230"
timestamp = dt.now().strftime("%Y-%m-%d %H_%M") timestamp = dt.now().strftime("%Y-%m-%d %H_%M")
default_name = f"{timestamp} DropboxBackupReport.xlsx" default_name = f"{timestamp} DropboxBackupReport.xlsx"
output = sys.argv[1] if len(sys.argv) > 1 else os.path.join(REPORT_DIR, default_name) output = sys.argv[1] if len(sys.argv) > 1 else os.path.join(REPORT_DIR, default_name)
+3
View File
@@ -0,0 +1,3 @@
@echo off
cd /d C:\Reporting\DropboxBackup
C:\Reporting\Python\python.exe main.py