From 7e8a68e73e15c2793adb6a0440a09063ee00953f Mon Sep 17 00:00:00 2001 From: Mr Finchum Date: Tue, 3 Dec 2024 22:50:34 +0000 Subject: [PATCH] Feature/updater can now update from gitlab Added update function, update function does not work inside git folder. --- CHANGELOG.md | 17 +++++++ ftl-savemanager.py | 122 +++++++++++++++++++++++++++++++++++++++------ latest_version.txt | 1 + 3 files changed, 125 insertions(+), 15 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 latest_version.txt diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..e252faa --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,17 @@ +# Changelog + +## 0.3.1 +Fixed that online version always was newer. Changed some small layouts from the menu. + +## 0.3.0 +Adding an update function. Checks the latest_version.txt file from gitlab and can pull the newest ftl-savemanager.py file and replace the local version. +If .gitignore file is found in the same folder update is denied, it also ask if the user wants to update before doing so. + +## 0.2.1 +Switching to Semantic versioning, MAJOR.MINOR.PATCH + +## 0.2 +Reorgenized: The functions have been reorganized into two classes, Backup and UI. + +## 0.1 +Initial working script diff --git a/ftl-savemanager.py b/ftl-savemanager.py index 7aa2217..ff5afe9 100644 --- a/ftl-savemanager.py +++ b/ftl-savemanager.py @@ -1,11 +1,15 @@ import os import time +import sys +import requests from datetime import datetime +version = "0.3.1" +gitlab_url = "https://gitlab.com/python_projects3802849/ftl-save-manager/-/raw/main" +settings_location = "settings.txt" 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() @@ -17,7 +21,7 @@ class BackupApp: 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") + print(f"Game location: {self.game_path}.\nSave location: {self.backup_path}") def read_settings(self): """Reading the settings file.""" @@ -29,15 +33,15 @@ class BackupApp: return(data) else: print("settings file does not exit.") - return 0 + return False def ask_for_path(self): """Uses get_path to request path from the user.""" - game_path = self.get_path("Game save path", 1) + game_path = self.get_path("Game save path", True) backup_path = self.get_path("Backup path") return(game_path, backup_path) - def get_path(self, str, ftl = 0): + def get_path(self, str, ftl = False): """Get path from user and check if exists.""" while True: path = input(f"please enter {str}: ") @@ -88,7 +92,7 @@ class BackupApp: 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) + time.sleep(0.5) def restore(self): """Copies a selected backup file to the game folder and renames it.""" @@ -99,7 +103,7 @@ class BackupApp: 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) + time.sleep(0.5) break elif choice == "b": break @@ -116,21 +120,21 @@ class BackupApp: try: print(f"Deleting {file}.") os.remove(f"{self.backup_path}/{file}") - time.sleep(0.1) + time.sleep(0.3) except Exception as e: print(f"Failed to delete {file}. Reason: {e}.") - time.sleep(2) + #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 + return True elif choice == "n": print(f"Deleting {str} files aborted") - time.sleep(1) - return 0 + #time.sleep(1) + return False else: print("Not a valid option, try again") @@ -144,7 +148,8 @@ class SimpleUI: """"Use dict to select an option (function).""" self.print_menu() while True: - choice = input("Selection Option: ") + choice = input("Select Option: ") + print("\n") if choice in self.options: self.options[choice]() self.print_menu() @@ -153,26 +158,113 @@ class SimpleUI: continue def print_menu(self): - print("\033[H\033[J", end="") + #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") +class Updater: + """Update local version from gitlab repo.""" + def __init__(self, version, update_url, local_file): + self.current_version = version + self.update_url = update_url + self.local_file = local_file + self.git = os.path.exists(".gitignore") # Checking if the program inside git env + def check_for_update(self): + """Check if a new version is available.""" + try: + print("Checking for updates...") + response = requests.get(f"{self.update_url}/latest_version.txt") + response.raise_for_status() + latest_version = response.text.strip() + + if self.compare_SemVer(latest_version): + print(f"New version {latest_version} available.\nCurrent version: {self.current_version}") + return True + else: + print("You are already on the latest version.") + return False + except Exception as e: + print(f"Error checking for updates: {e}") + return False + + def compare_SemVer(self, latest_version): + """Compare two Semantic versioning strings.""" + local_version = self.convert_str_list_to_int(self.current_version.split(".")) + online_version = self.convert_str_list_to_int(latest_version.split(".")) + return local_version < online_version + + def convert_str_list_to_int(self, list_str): + """Converts a list with strings to a list with intengers.""" + return [int(i) for i in list_str] + + def download_update(self, latest_version): + """Download the new version and overwrite the current file.""" + try: + print(f"Downloading version {latest_version}...") + response = requests.get(f"{self.update_url}/{self.local_file}") + response.raise_for_status() + with open(self.local_file, "wb") as file: + file.write(response.content) + print("Update downloaded.") + return True + except Exception as e: + print(f"Error downloading update: {e}") + return False + + def restart_program(self): + """Restart the current program.""" + print("Restarting the program...") + os.execv(sys.executable, ["python"] + sys.argv) + + def run_update(self): + """Main method to check, update, and restart.""" + if self.git: + print("Updating only works outside git env.") + else: + latest_version = self.check_for_update() + if latest_version: + if self.yes_no("Do you want to update the program?"): + success = self.download_update(latest_version) + if success: + self.restart_program() + else: + print("Update cancelled") + return + + def yes_no(self, str): + """Ask user y/n question""" + while True: + choice = input(f"{str} (y/n)") + if choice == "y": + return True + elif choice == "n": + return False + else: + print("Not a valid option, try again") + +up = Updater(version, gitlab_url, "ftl-savemanager.py") +app = BackupApp(settings_location) + +# Designing the menu options from the backupapp and the layout menu_options = { "b": app.backup, "r": app.restore, "d": app.delete_backup_file, + "u": up.run_update, "q": exit } menu_names = { "b": "Backup now", "r": "Restore backup", "d": "Delete backup files", + "u": "Update", "q": "Exit" } +# Loading the ui and the options sui = SimpleUI(menu_options, menu_names) +# starting the ui i.e. program sui.menu() diff --git a/latest_version.txt b/latest_version.txt new file mode 100644 index 0000000..9e11b32 --- /dev/null +++ b/latest_version.txt @@ -0,0 +1 @@ +0.3.1