Functions are split into classes.
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.
This commit is contained in:
parent
0aedfb266e
commit
a9ecd949db
3 changed files with 180 additions and 167 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1 +1,3 @@
|
|||
local_files/
|
||||
.ropeproject/
|
||||
__pycache__/
|
||||
|
|
|
@ -1,167 +0,0 @@
|
|||
import os
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
version = 0.01
|
||||
settings_path = "local_files/settings.txt" # containes two lines, first path of game, second path of savelocation
|
||||
backup_files = {"1": "initiating"} # initiating global variable to be used in functions.
|
||||
|
||||
def menu(options, names):
|
||||
""""Use dict to select an option (function)."""
|
||||
print_menu(options, names)
|
||||
while True:
|
||||
choice = input("Selection Option: ")
|
||||
if choice in options:
|
||||
options[choice]()
|
||||
print_menu(options, names)
|
||||
else:
|
||||
print("Try again")
|
||||
continue
|
||||
|
||||
def print_menu(options, names):
|
||||
#print("\033[H\033[J", end="")
|
||||
print("Menu options")
|
||||
for key in options:
|
||||
print(f"{key}: {names[key]}")
|
||||
|
||||
def copy_file(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():
|
||||
""""List all files inside the backup folder."""
|
||||
files = os.listdir(backup_path)
|
||||
files.reverse() # this way the newest file is the top one.
|
||||
backup_files.clear()
|
||||
i = 1
|
||||
for file in files:
|
||||
new_item = {f"{i}": file}
|
||||
print(f"[{i}] {file}")
|
||||
backup_files.update(new_item)
|
||||
i += 1
|
||||
|
||||
def backup():
|
||||
"""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")
|
||||
copy_file(f"{game_path}/continue.sav", f"{backup_path}/{formatted_now}.bkup")
|
||||
time.sleep(1)
|
||||
|
||||
def restore():
|
||||
"""Copies a selected backup file to the game folder."""
|
||||
print("Select a file to restore (back [b]):")
|
||||
list_backup()
|
||||
while True:
|
||||
choice = input("Enter number (b = back): ")
|
||||
if choice in backup_files:
|
||||
copy_file(f"{backup_path}/{backup_files[choice]}", f"{game_path}/continue.sav")
|
||||
print(f"{backup_files[choice]} restored.")
|
||||
time.sleep(1)
|
||||
break
|
||||
elif choice == "b":
|
||||
break
|
||||
else:
|
||||
print("not a valid option")
|
||||
continue
|
||||
|
||||
def delete_backup_file():
|
||||
# Making a yes/no function would be cleaner
|
||||
if yes_no("Backup"):
|
||||
for file in os.listdir(backup_path):
|
||||
if file[-5:] == ".bkup":
|
||||
try:
|
||||
print(f"Deleting {file}.")
|
||||
os.remove(f"{backup_path}/{file}")
|
||||
time.sleep(1)
|
||||
except Exception as e:
|
||||
print(f"Failed to delete {file}. Reason: {e}.")
|
||||
time.sleep(3)
|
||||
|
||||
def yes_no(str):
|
||||
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")
|
||||
|
||||
def read_settings():
|
||||
if os.path.exists(settings_path):
|
||||
with open(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():
|
||||
paths = ["game_path", "backup_path"]
|
||||
|
||||
paths[0] = get_path("Absolut save path")
|
||||
paths[1] = get_path("Backup path")
|
||||
return(paths)
|
||||
|
||||
def get_path(str):
|
||||
while True:
|
||||
path = input(f"please enter {str}: ")
|
||||
if os.path.exists(path):
|
||||
break
|
||||
else:
|
||||
print("Path not valid, try again.")
|
||||
continue
|
||||
return path
|
||||
|
||||
def write_settings(list):
|
||||
with open(settings_path, "w") as f:
|
||||
for item in list:
|
||||
f.write(item + "\n")
|
||||
|
||||
print("write settings.")
|
||||
|
||||
settings = read_settings()
|
||||
if settings != False:
|
||||
game_path = settings[0]
|
||||
backup_path = settings[1]
|
||||
if not os.path.exists(f"{game_path}/ae_prof.sav") and not os.path.exists(backup_path):
|
||||
print("Path in settings file are incorrect.")
|
||||
paths = ask_for_path()
|
||||
game_path = paths[0]
|
||||
backup_path = paths[1]
|
||||
write_settings((game_path, backup_path))
|
||||
else:
|
||||
paths = ask_for_path()
|
||||
game_path = paths[0]
|
||||
backup_path = paths[1]
|
||||
write_settings((game_path, backup_path))
|
||||
|
||||
print(f"Game location: {game_path}.\nSave loction: {backup_path}\n")
|
||||
time.sleep(1)
|
||||
|
||||
menu_options = {
|
||||
"b": backup,
|
||||
"r": restore,
|
||||
"d": delete_backup_file,
|
||||
"q": exit
|
||||
}
|
||||
menu_names = {
|
||||
"b": "Backup now",
|
||||
"r": "Restore backup",
|
||||
"d": "Delete backup files",
|
||||
"q": "Exit"
|
||||
}
|
||||
menu(menu_options, menu_names)
|
||||
|
||||
|
||||
# TODO
|
||||
# Backup profile
|
||||
# updater
|
178
ftl-savemanager.py
Normal file
178
ftl-savemanager.py
Normal file
|
@ -0,0 +1,178 @@
|
|||
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()
|
Loading…
Add table
Add a link
Reference in a new issue