Skip to content

Commit 792f6d6

Browse files
authored
Merge pull request #22 from Cyber-Syntax/feat/add-dirs-to-config
refactor!: move backup and ignore paths to config.json
2 parents d9edad1 + 887701d commit 792f6d6

7 files changed

Lines changed: 169 additions & 65 deletions

File tree

README.md

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,27 @@
5050
source .venv/bin/activate
5151
```
5252

53-
5. You need do before start:
53+
5. Config File Handling:
54+
To customize your backup settings, you can use a `config.json` file. This file allows you to specify:
5455

55-
- You need to change example directories on the **example-dirs_to_backup.txt** and rename it to **dirs_to_backup.txt**.
56+
- Backup folder location, Directories to back up, Directories to ignore, Number of tar.xz and tar.xz.enc files to keep
57+
**Creating a Config File:**
58+
You have two options:
5659

57-
6. Start the script:
60+
1. **Run the script and follow the on-screen instructions**. This will guide you through creating a `config.json` file.
61+
2. **Use an example config file (Optional)**:
62+
63+
- Copy the example configuration from `config_files_example/config.json`
64+
- Paste it into your `backup_folder/config_files/config.json` location (e.g `~/Documents/backup-for-cloud/config_files/config.json)
65+
- Modify it as needed
66+
67+
3. Start the script:
5868

5969
```bash
6070
python3 main.py
6171
```
6272

63-
7. Follow the on-screen instructions.
73+
4. Follow the on-screen instructions.
6474

6575
---
6676

README.tr.md

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,25 @@
5050
source .venv/bin/activate
5151
```
5252

53-
5. Başlamadan önce yapmanız gerekenler:
53+
5. Yapılandırma dosyası ayarlama:
54+
Yedeklemeye ait ayarlarınızı özelleştirmek için, bir `config.json` dosyasını kullanabilirsiniz. Bu dosya size şunları belirtmenizi sağlar:
5455

55-
- **example-dirs_to_backup.txt** dosyasındaki örnek **dizinleri** değiştirmelisiniz ve adını **dirs_to_backup.txt** olarak değiştirmelisiniz.
56+
- Yedekleme klasörünün konumu, Geri yüklenecek dizinler, İlgisiz bırakılacak dizinler, Saklanacak tar.xz ve tar.xz.enc dosyalarının sayısı
57+
**Ayar Dosyası Oluşturma:**
58+
İki seçeneğiniz vardır:
5659

57-
6. Script'i başlatın:
60+
1. **Senaryoyu çalıştır ve ekrandaki talimatları takip et**. Bu size bir `config.json` dosyası oluşturmayı kılavuzlayacaktır.
61+
2. **Örnek Ayar Dosyasını Kullan (Opsiyonel)**:
62+
- Örnek konfigürasyonunuzu `config_files_example/config.json` konumundan kopyalayın
63+
- Bu dosyayı `backup_folder/config_files/config.json` konumuna yapıştırın (örn. `~/Documents/backup-for-cloud/config_files/config.json`)
64+
- Gereksinim duyduğunuz kadarını değiştirin
65+
3. Script'i başlatın:
5866

5967
```bash
6068
python3 main.py
6169
```
6270

63-
7. Ekrandaki talimatları izleyin.
71+
4. Ekrandaki talimatları izleyin.
6472

6573
---
6674

config_files_example/config.json

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,18 @@
11
{
2-
"backup_folder": "~/Documents/backup-for-cloud/",
3-
"keep_backup": 1,
4-
"keep_enc_backup": 1,
5-
"dirs_file_path": "dirs_to_backup.txt",
6-
"ignore_file_path": "ignore.txt"
7-
}
2+
"backup_folder": "~/Documents/backup-for-cloud/",
3+
"keep_backup": 1,
4+
"keep_enc_backup": 1,
5+
"dirs_to_backup": [
6+
"~/Documents/",
7+
"~/dotfiles/",
8+
"~/bare/",
9+
"~/Pictures/",
10+
"~/Music/"
11+
],
12+
"ignore_list": [
13+
"~/Documents/appimages",
14+
"~/Documents/backup-for-cloud",
15+
"~/Documents/linuxISO"
16+
]
17+
}
18+

example-dirs_to_backup.txt

Lines changed: 0 additions & 4 deletions
This file was deleted.

ignore.txt

Lines changed: 0 additions & 4 deletions
This file was deleted.

main.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,15 @@ def main():
1616
backup_manager = BackupManager()
1717

1818
if not os.path.isfile(backup_manager.config_file_path):
19+
logging.info("Configuration file not found. Creating a new one.")
1920
backup_manager.ask_inputs()
2021
backup_manager.save_credentials()
2122
else:
2223
backup_manager.load_credentials()
24+
# Check if directories to backup are configured
25+
if not backup_manager.dirs_to_backup:
26+
logging.warning("No directories found in configuration. Setting up now.")
27+
backup_manager.configure_directories()
2328

2429
# Create a loop that will run until the user enters 6 to exit
2530
while True:

src/backup_manager.py

Lines changed: 121 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -23,32 +23,25 @@ class BackupManager:
2323
backup_folder: str = field(default_factory=lambda: "~/Documents/backup-for-cloud/")
2424
keep_backup: int = field(default_factory=lambda: 1)
2525
keep_enc_backup: int = field(default_factory=lambda: 1)
26-
dirs_file_path: str = field(default_factory=lambda: "dirs_to_backup.txt")
27-
ignore_file_path: str = field(default_factory=lambda: "ignore.txt")
26+
dirs_to_backup: List[str] = field(default_factory=list)
27+
ignore_list: List[str] = field(default_factory=list)
2828
config_file_path: str = field(init=False)
29-
30-
current_date: str = datetime.datetime.now().strftime("%d-%m-%Y")
3129
backup_file_path: str = field(init=False)
3230
decrypt_file_path: str = field(init=False)
33-
# size calculator
34-
dirs_to_backup: List[str] = field(init=False)
35-
ignore_list: List[str] = field(init=False)
31+
current_date: str = datetime.datetime.now().strftime("%d-%m-%Y")
3632

3733
def __post_init__(self):
3834
self.backup_file_path = os.path.expanduser(
3935
f"{self.backup_folder}/{self.current_date}.tar.xz"
4036
)
41-
# self.backup_folder = os.path.expanduser(self.backup_folder)
42-
43-
# TODO: save backup folder without expanduser
4437
self.config_file_path = os.path.join(
4538
os.path.expanduser(self.backup_folder), "config_files", "config.json"
4639
)
4740
# encryption
4841
self.decrypt_file_path = os.path.join(self.backup_folder, "decrypted.tar.xz")
49-
# size calculator
50-
self.dirs_to_backup = self.read_dirs_to_backup()
51-
self.ignore_list = self.read_ignore_list()
42+
# # size calculator
43+
# self.dirs_to_backup = []
44+
# self.ignore_list = []
5245

5346
def ask_inputs(self):
5447
while True:
@@ -75,6 +68,102 @@ def ask_inputs(self):
7568
except ValueError:
7669
print("Invalid input, please enter a valid integer.")
7770

71+
print("\nNow let's configure the directories for backup.")
72+
self.configure_directories()
73+
74+
def configure_directories(self):
75+
"""Interactive method to add directories to backup and ignore lists."""
76+
print("=== Configure Directories to Backup ===")
77+
while True:
78+
print("\nCurrent directories to backup:")
79+
for idx, directory in enumerate(self.dirs_to_backup, start=1):
80+
print(f"{idx}. {directory}")
81+
print("\nOptions:")
82+
print("1. Add directories (separate multiple entries with commas)")
83+
print("2. Remove a directory")
84+
print("3. View ignore list")
85+
print("4. Modify ignore list")
86+
print("5. Finish and save")
87+
88+
choice = input("Choose an option (1-5): ").strip()
89+
if choice == "1":
90+
dirs_input = input(
91+
"Enter directories to add (separate multiple entries with commas): "
92+
).strip()
93+
new_dirs = [d.strip() for d in dirs_input.split(",") if d.strip()]
94+
# Avoid duplicates and invalid entries
95+
added_dirs = [d for d in new_dirs if d not in self.dirs_to_backup]
96+
self.dirs_to_backup.extend(added_dirs)
97+
print(f"Added directories: {', '.join(added_dirs)}")
98+
elif choice == "2":
99+
print("Select a directory to remove:")
100+
for idx, directory in enumerate(self.dirs_to_backup, start=1):
101+
print(f"{idx}. {directory}")
102+
try:
103+
idx_to_remove = int(input("Enter the number to remove: ").strip())
104+
if 0 < idx_to_remove <= len(self.dirs_to_backup):
105+
removed_dir = self.dirs_to_backup.pop(idx_to_remove - 1)
106+
print(f"Removed: {removed_dir}")
107+
else:
108+
print("Invalid selection.")
109+
except ValueError:
110+
print("Invalid input.")
111+
elif choice == "3":
112+
print("Current ignore list:")
113+
for idx, path in enumerate(self.ignore_list, start=1):
114+
print(f"{idx}. {path}")
115+
elif choice == "4":
116+
self.modify_ignore_list()
117+
elif choice == "5":
118+
self.save_credentials()
119+
print("Configuration saved.")
120+
break
121+
else:
122+
print("Invalid choice. Please try again.")
123+
124+
def modify_ignore_list(self):
125+
"""Interactive method to add or remove paths from the ignore list."""
126+
print("=== Modify Ignore List ===")
127+
while True:
128+
print("\nCurrent ignore list:")
129+
for idx, path in enumerate(self.ignore_list, start=1):
130+
print(f"{idx}. {path}")
131+
132+
print("\nOptions:")
133+
print("1. Add ignore paths (separate multiple entries with commas)")
134+
print("2. Remove an ignore path")
135+
print("3. Finish and save")
136+
137+
choice = input("Choose an option (1-3): ").strip()
138+
if choice == "1":
139+
ignore_input = input(
140+
"Enter paths to ignore (separate multiple entries with commas): "
141+
).strip()
142+
new_ignores = [p.strip() for p in ignore_input.split(",") if p.strip()]
143+
# Avoid duplicates and invalid entries
144+
added_ignores = [p for p in new_ignores if p not in self.ignore_list]
145+
self.ignore_list.extend(added_ignores)
146+
print(f"Added ignore paths: {', '.join(added_ignores)}")
147+
elif choice == "2":
148+
print("Select an ignore path to remove:")
149+
for idx, path in enumerate(self.ignore_list, start=1):
150+
print(f"{idx}. {path}")
151+
try:
152+
idx_to_remove = int(input("Enter the number to remove: ").strip())
153+
if 0 < idx_to_remove <= len(self.ignore_list):
154+
removed_path = self.ignore_list.pop(idx_to_remove - 1)
155+
print(f"Removed from ignore list: {removed_path}")
156+
else:
157+
print("Invalid selection.")
158+
except ValueError:
159+
print("Invalid input.")
160+
elif choice == "3":
161+
self.save_credentials()
162+
print("Ignore list updated and saved.")
163+
break
164+
else:
165+
print("Invalid choice. Please try again.")
166+
78167
def save_credentials(self):
79168
"""Save the credentials to a file in json format from response"""
80169

@@ -86,11 +175,11 @@ def save_credentials(self):
86175
"backup_folder": self.backup_folder,
87176
"keep_backup": self.keep_backup,
88177
"keep_enc_backup": self.keep_enc_backup,
89-
"dirs_file_path": self.dirs_file_path,
90-
"ignore_file_path": self.ignore_file_path,
178+
"dirs_to_backup": self.dirs_to_backup,
179+
"ignore_list": self.ignore_list,
91180
}
92181

93-
with open(self.config_file_path, "w") as config_file:
182+
with open(self.config_file_path, "w", encoding="utf-8") as config_file:
94183
json.dump(config, config_file, indent=4)
95184

96185
print(f"Configuration file created at {self.config_file_path}")
@@ -109,15 +198,15 @@ def load_credentials(self):
109198
self.backup_folder = config.get("backup_folder", self.backup_folder)
110199
self.keep_backup = config.get("keep_backup", self.keep_backup)
111200
self.keep_enc_backup = config.get("keep_enc_backup", self.keep_enc_backup)
112-
self.dirs_file_path = config.get("dirs_file_path", self.dirs_file_path)
113-
self.ignore_file_path = config.get(
114-
"ignore_file_path", self.ignore_file_path
115-
)
201+
self.dirs_to_backup = config.get("dirs_to_backup", self.dirs_to_backup)
202+
self.ignore_list = config.get("ignore_list", self.ignore_list)
116203

117204
print(f"Configuration loaded from {self.config_file_path}")
118205
print(f"Backup folder set to {self.backup_folder}")
119206
print(f"Keep backup: {self.keep_backup}")
120207
print(f"Keep encrypted backup: {self.keep_enc_backup}")
208+
print(f"Directories to backup: {self.dirs_to_backup}")
209+
print(f"Ignore list: {self.ignore_list}")
121210
else:
122211
print(f"Configuration file {self.config_file_path} not found.")
123212

@@ -128,22 +217,13 @@ def check_backup_exist(self) -> bool:
128217
def backup_directories(self) -> bool:
129218
"""Backup the directories listed in dirs_to_backup.txt to a compressed file"""
130219

131-
# Read in the directories to backup from the file
132-
dirs_to_backup: List[str] = []
133-
with open(self.dirs_file_path, "r", encoding="utf-8") as file:
134-
for line in file:
135-
directory = line.strip()
136-
if directory:
137-
dirs_to_backup.append(directory)
220+
if not self.dirs_to_backup:
221+
print(
222+
"No directories to backup. Create directories on the config.json. Look example config.json."
223+
)
224+
sys.exit()
138225

139-
# Read and expand paths in the ignore file
140-
ignore_paths = []
141-
if os.path.isfile(self.ignore_file_path):
142-
with open(self.ignore_file_path, "r", encoding="utf-8") as file:
143-
for line in file:
144-
ignore_path = line.strip()
145-
if ignore_path:
146-
ignore_paths.append(os.path.expanduser(ignore_path))
226+
ignore_paths = [os.path.expanduser(path) for path in self.ignore_list]
147227

148228
# Generate the exclude options for the tar command
149229
exclude_options = " ".join([f"--exclude={path}" for path in ignore_paths])
@@ -152,13 +232,11 @@ def backup_directories(self) -> bool:
152232
filesystem_option = "--one-file-system"
153233

154234
# Expand the user's home directory for each directory to backup
155-
dir_paths = [os.path.expanduser(path) for path in dirs_to_backup]
235+
dir_paths = [os.path.expanduser(path) for path in self.dirs_to_backup]
156236

157237
# Calculate the total size using SizeCalculator
158-
size_calculator = BackupManager(self.dirs_file_path, self.ignore_file_path)
159-
total_size_bytes = size_calculator.calculate_total_backup_size(
160-
dirs_to_backup, size_calculator.ignore_list
161-
)
238+
# size_calculator = BackupManager(self.dirs_to_backup, self.ignore_list)
239+
total_size_bytes = self.calculate_total_backup_size(dir_paths, ignore_paths)
162240

163241
# Convert the total size to MB and GiB
164242
total_size_mb = total_size_bytes / (1024 * 1024)
@@ -409,7 +487,7 @@ def delete_old_backups(self):
409487
def read_dirs_to_backup(self) -> List[str]:
410488
"""Read the directories and files listed in dirs_to_backup.txt"""
411489
dirs_to_backup = []
412-
with open(self.dirs_file_path, "r", encoding="utf-8") as file:
490+
with open(self.dirs_to_backup, "r", encoding="utf-8") as file:
413491
for line in file:
414492
directory = line.strip()
415493
if directory:
@@ -419,8 +497,8 @@ def read_dirs_to_backup(self) -> List[str]:
419497
def read_ignore_list(self) -> List[str]:
420498
"""Read the directories and files listed in ignore.txt"""
421499
ignore_list = []
422-
if os.path.isfile(self.ignore_file_path):
423-
with open(self.ignore_file_path, "r", encoding="utf-8") as file:
500+
if os.path.isfile(self.ignore_list):
501+
with open(self.ignore_list, "r", encoding="utf-8") as file:
424502
for line in file:
425503
ignore_path = line.strip()
426504
if ignore_path:

0 commit comments

Comments
 (0)