Skip to content

Commit f3cac91

Browse files
committed
feat: Add core utilities, constants, and consolidate imports
1 parent 59e1cc1 commit f3cac91

1 file changed

Lines changed: 61 additions & 12 deletions

File tree

Windows Backup/windows_backup.py

Lines changed: 61 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
import re
22
import sys
3+
import os
4+
import shutil
5+
import time
6+
import hashlib
7+
import datetime
8+
import json
9+
import threading
10+
import random
11+
from concurrent.futures import ThreadPoolExecutor, as_completed
12+
313
try:
414
import plistlib
515
except ImportError:
@@ -11,31 +21,70 @@
1121
import winreg
1222
except ImportError:
1323
winreg = None
14-
import os, shutil, time, hashlib, datetime, json, threading, random
24+
else:
25+
winreg = None
26+
27+
# Optional pywin32
1528
try:
16-
import win32api, win32con
17-
except ImportError:
29+
import win32api, win32con # type: ignore
30+
except Exception:
1831
win32api = win32con = None
32+
1933
from PySide6.QtWidgets import (
2034
QMainWindow, QApplication, QPushButton, QWidget, QFileDialog, QMessageBox,
21-
QGridLayout, QProgressDialog, QSpacerItem, QSizePolicy
35+
QGridLayout, QProgressDialog, QSpacerItem, QSizePolicy, QCheckBox,
36+
QDialog, QVBoxLayout, QListWidget, QAbstractItemView, QTableWidget,
37+
QTableWidgetItem, QLabel
2238
)
23-
from PySide6.QtWidgets import QSizePolicy
2439
from PySide6.QtCore import Qt, QThread, Signal, QTimer
2540

41+
# -----------------------------------------------------------------------------
42+
# Constants & helpers
43+
# -----------------------------------------------------------------------------
44+
USER_ROOT = os.path.expandvars(r"C:\\Users") if sys.platform.startswith("win") else "/Users"
45+
TIMESTAMP_FMT = "%Y-%m-%d_%H-%M-%S"
2646

2747

28-
def get_user_path(subpath=""):
29-
"""Return full path inside user's profile directory."""
30-
user_profile = os.environ.get("USERPROFILE", "")
31-
return os.path.join(user_profile, subpath) if subpath else user_profile
48+
def ensure_logs_dir(root_dir: str) -> str:
49+
"""Create and return a _logs directory inside root_dir."""
50+
logs = os.path.join(root_dir, "_logs")
51+
os.makedirs(logs, exist_ok=True)
52+
return logs
3253

3354

55+
def new_log_file(root_dir: str, prefix: str) -> str:
56+
ts = datetime.datetime.now().strftime(TIMESTAMP_FMT)
57+
logs_dir = ensure_logs_dir(root_dir)
58+
return os.path.join(logs_dir, f"{prefix}_{ts}.log")
59+
60+
61+
def log_line(path: str, text: str) -> None:
62+
try:
63+
with open(path, "a", encoding="utf-8") as f:
64+
f.write(text.rstrip("\n") + "\n")
65+
except Exception:
66+
pass
67+
68+
69+
def chunked_file_hash(filepath: str, chunk_size: int = 1024 * 1024) -> str:
70+
hasher = hashlib.md5()
71+
with open(filepath, "rb") as f:
72+
while True:
73+
chunk = f.read(chunk_size)
74+
if not chunk:
75+
break
76+
hasher.update(chunk)
77+
return hasher.hexdigest()
78+
79+
80+
# -----------------------------------------------------------------------------
81+
# Backup worker thread (multithreaded copy inside)
82+
# -----------------------------------------------------------------------------
3483
class BackupWorker(QThread):
35-
progress_update = Signal(int, str, int, int, float)
36-
finished = Signal(int, bool, str)
84+
progress_update = Signal(int, str, int, int, float) # files_processed, file, file_size, copied_size, speed
85+
finished = Signal(int, bool, str) # files_copied, success, message
3786

38-
def __init__(self, source_dir, destination_root, patterns):
87+
def __init__(self, source_dir: str, destination_root: str, patterns, log_file: str, max_workers: int | None = None):
3988
super().__init__()
4089
self.source_dir = source_dir
4190
self.destination_root = destination_root

0 commit comments

Comments
 (0)