Instead of having all code written as functions, we're now structuring it around two main classes: the Backup class, responsible for handling backups, and the UI class, which focuses on displaying and initiating calls to backup functions.
178 lines
5.9 KiB
Python
178 lines
5.9 KiB
Python
import os
|
|
import time
|
|
from datetime import datetime
|
|
|
|
class BackupApp:
|
|
""""Backup class, copy FTL continue file to backup location and restores it."""
|
|
def __init__(self, settings_file_path):
|
|
self.version = 0.1
|
|
self.backup_files = {"1": "initiating"} # initiating variable
|
|
self.settings_path = settings_file_path
|
|
self.settings = self.read_settings()
|
|
|
|
if self.settings:
|
|
self.game_path = self.settings[0]
|
|
self.backup_path = self.settings[1]
|
|
else:
|
|
self.game_path, self.backup_path = self.ask_for_path()
|
|
self.write_settings([self.game_path, self.backup_path])
|
|
|
|
print(f"Game location: {self.game_path}.\nSave location: {self.backup_path}\n")
|
|
|
|
def read_settings(self):
|
|
"""Reading the settings file."""
|
|
if os.path.exists(self.settings_path):
|
|
with open(self.settings_path, "r") as f:
|
|
data = f.readlines()
|
|
for i in range(len(data)):
|
|
data[i] = data[i].strip() #stripping white space
|
|
return(data)
|
|
else:
|
|
print("settings file does not exit.")
|
|
return 0
|
|
|
|
def ask_for_path(self):
|
|
"""Uses get_path to request path from the user."""
|
|
game_path = self.get_path("Game save path", 1)
|
|
backup_path = self.get_path("Backup path")
|
|
return(game_path, backup_path)
|
|
|
|
def get_path(self, str, ftl = 0):
|
|
"""Get path from user and check if exists."""
|
|
while True:
|
|
path = input(f"please enter {str}: ")
|
|
if ftl:
|
|
profile_file = path + "/ae_prof.sav"
|
|
if os.path.isfile(profile_file):
|
|
return path
|
|
else:
|
|
print("Path is not valid, try again.")
|
|
continue
|
|
if os.path.exists(path):
|
|
return path
|
|
break
|
|
else:
|
|
print("Path not valid, try again.")
|
|
continue
|
|
return path
|
|
|
|
def write_settings(self, list):
|
|
"""Write the settings (path)"""
|
|
with open(self.settings_path, "w") as f:
|
|
for item in list:
|
|
f.write(item + "\n")
|
|
print("write settings.")
|
|
|
|
def copy_file(self, src, dst):
|
|
""""Simply copies a file from a to b."""
|
|
if not os.path.isfile(src):
|
|
print(f"File to copy {src} does not exist.")
|
|
return False
|
|
os.system(f"cp {src} {dst}")
|
|
print(f"Wrote: {dst}")
|
|
|
|
def list_backup(self):
|
|
""""List all files inside the backup folder."""
|
|
files = os.listdir(self.backup_path)
|
|
files.reverse() # this way the newest file is the top one.
|
|
self.backup_files.clear()
|
|
i = 1
|
|
for file in files:
|
|
new_item = {f"{i}": file}
|
|
print(f"[{i}] {file}")
|
|
self.backup_files.update(new_item)
|
|
i += 1
|
|
|
|
def backup(self):
|
|
"""Copies the game file to the backup folder and adds date-time stamp."""
|
|
now = datetime.now()
|
|
formatted_now = now.strftime("%Y-%m-%d_%H-%M")
|
|
self.copy_file(f"{self.game_path}/continue.sav", f"{self.backup_path}/{formatted_now}.bkup")
|
|
time.sleep(1)
|
|
|
|
def restore(self):
|
|
"""Copies a selected backup file to the game folder and renames it."""
|
|
print("Select a file to restore (back [b]):")
|
|
self.list_backup()
|
|
while True:
|
|
choice = input("Enter number (b = back): ")
|
|
if choice in self.backup_files:
|
|
self.copy_file(f"{self.backup_path}/{self.backup_files[choice]}", f"{self.game_path}/continue.sav")
|
|
print(f"{self.backup_files[choice]} restored.")
|
|
time.sleep(1)
|
|
break
|
|
elif choice == "b":
|
|
break
|
|
else:
|
|
print("not a valid option")
|
|
continue
|
|
|
|
def delete_backup_file(self):
|
|
"""Deltes all .bkup files in the backup folder."""
|
|
# Making a yes/no function would be cleaner
|
|
if self.yes_no("Backup"):
|
|
for file in os.listdir(self.backup_path):
|
|
if file[-5:] == ".bkup":
|
|
try:
|
|
print(f"Deleting {file}.")
|
|
os.remove(f"{self.backup_path}/{file}")
|
|
time.sleep(0.1)
|
|
except Exception as e:
|
|
print(f"Failed to delete {file}. Reason: {e}.")
|
|
time.sleep(2)
|
|
|
|
def yes_no(self, str):
|
|
"""Ask user y/n question regaring deletion of a string."""
|
|
while True:
|
|
choice = input(f"Are you sure you want to delete all {str} files? (y/n)")
|
|
if choice == "y":
|
|
return 1
|
|
elif choice == "n":
|
|
print(f"Deleting {str} files aborted")
|
|
time.sleep(1)
|
|
return 0
|
|
else:
|
|
print("Not a valid option, try again")
|
|
|
|
class SimpleUI:
|
|
"""'UI for the backup class, simply displayes a menu and accepts letter to choice option."""
|
|
def __init__(self, options, names):
|
|
self.options = options
|
|
self.names = names
|
|
|
|
def menu(self):
|
|
""""Use dict to select an option (function)."""
|
|
self.print_menu()
|
|
while True:
|
|
choice = input("Selection Option: ")
|
|
if choice in self.options:
|
|
self.options[choice]()
|
|
self.print_menu()
|
|
else:
|
|
print("Try again")
|
|
continue
|
|
|
|
def print_menu(self):
|
|
print("\033[H\033[J", end="")
|
|
print("Menu options")
|
|
for key in self.options:
|
|
print(f"{key}: {self.names[key]}")
|
|
|
|
app = BackupApp("local_files/settings.txt")
|
|
|
|
menu_options = {
|
|
"b": app.backup,
|
|
"r": app.restore,
|
|
"d": app.delete_backup_file,
|
|
"q": exit
|
|
}
|
|
menu_names = {
|
|
"b": "Backup now",
|
|
"r": "Restore backup",
|
|
"d": "Delete backup files",
|
|
"q": "Exit"
|
|
}
|
|
|
|
sui = SimpleUI(menu_options, menu_names)
|
|
|
|
sui.menu()
|