From 018d598cde5109bad42a365b2ef30f3e5c9f221e Mon Sep 17 00:00:00 2001 From: Zehen-249 <1hasanmehendi123@gmail.com> Date: Wed, 20 Aug 2025 11:00:25 +0530 Subject: [PATCH 01/11] [Refactor] update_version_check.py --- src/osdag/update_version_check.py | 88 +++++++++++++++---------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/src/osdag/update_version_check.py b/src/osdag/update_version_check.py index 312cff3a7..8cab2fb53 100644 --- a/src/osdag/update_version_check.py +++ b/src/osdag/update_version_check.py @@ -1,55 +1,55 @@ ######################### UpDateNotifi ################ + import urllib.request -import requests import re -from PyQt5.QtWidgets import QMessageBox,QMainWindow -import sys +from pathlib import Path + +version_file = Path(__file__).parent / "_version.py" +ns = {} +exec(version_file.read_text(), ns) +curr_version = ns["__version__"] class Update(): + URL = "https://osdag.fossee.in/resources/downloads" + PATTERN = re.compile(r'Install Osdag \((v[0-9a-zA-Z_]+)\)') + def __init__(self): super().__init__() - self.old_version=self.get_current_version() - # msg = self.notifi() + self.old_version = curr_version - def notifi(self): + def fetch_latest_version(self) -> str: + """Fetch the latest version string from Osdag downloads page.""" try: - url = "https://raw.githubusercontent.com/osdag-admin/Osdag/master/README.md" - file = urllib.request.urlopen(url) - version = 'not found' - for line in file: - decoded_line = line.decode("utf-8") - match = re.search(r'Download the latest release version (\S+)', decoded_line) - if match: - version = match.group(1) - version = version.split("<")[0] - break - # decoded_line = line.decode("utf-8") - # new_version = decoded_line.split("=")[1] - if version != self.old_version: - msg = 'Current version: '+ self.old_version+'
'+'Latest version '+ str(version)+'
'+\ - 'Update will be available here ' - else: - msg = 'Already up to date' - return msg - except: - return "No internet connection" - - def get_current_version(self): - version_file = "_version.py" - rel_path = str(sys.path[0]) - rel_path = rel_path.replace("\\", "/") - VERSIONFILE = rel_path +'/'+ version_file + with urllib.request.urlopen(self.URL) as response: + for line in response: + decoded_line = line.decode("utf-8") + match = self.PATTERN.search(decoded_line) + if match: + return match.group(1) + return "not found" + except Exception as e: + raise ConnectionError(f"Error fetching latest version: {e}") + def notifi(self) -> str: + """Compare current version with latest version and return update message.""" try: - verstrline = open(VERSIONFILE, "rt").read() - except EnvironmentError: - pass # Okay, there is no version file. - else: - VSRE = r"^__version__ = ['\"]([^'\"]*)['\"]" - mo = re.search(VSRE, verstrline, re.M) - if mo: - verstr = mo.group(1) - return verstr - else: - print("unable to find version in %s" % (VERSIONFILE,)) - raise RuntimeError("if %s.py exists, it is required to be well-formed" % (VERSIONFILE,)) + latest_version = self.fetch_latest_version().lstrip("v").replace("_", ".") + print(f"Latest version found: {latest_version}") + + if latest_version == "not found": + return "Could not determine latest version." + + if latest_version != self.old_version: + return ( + f"Current version: {self.old_version}
" + f"Latest version: {latest_version}
" + f'Update will be available ' + f'
here' + ) + + return "Already up to date" + + except Exception as e: + return str(e) + + From 935e3c925d0b767122ad911717cd6ba214f11950 Mon Sep 17 00:00:00 2001 From: Zehen-249 <1hasanmehendi123@gmail.com> Date: Wed, 20 Aug 2025 13:35:45 +0530 Subject: [PATCH 02/11] Add update_now and update_later buttons --- src/osdag/osdagMainPage.py | 23 ++++++++++++++++++++++- src/osdag/update_version_check.py | 4 ---- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/osdag/osdagMainPage.py b/src/osdag/osdagMainPage.py index 0cfc08b51..845d81da9 100644 --- a/src/osdag/osdagMainPage.py +++ b/src/osdag/osdagMainPage.py @@ -511,7 +511,28 @@ def selection_change(self): elif loc == "Check for Update": update_class = Update() msg = update_class.notifi() - QMessageBox.information(self, 'Info',msg) + # QMessageBox.information(self, 'Info',msg) + + box = QMessageBox(self) + box.setIcon(QMessageBox.Information) + box.setWindowTitle("Check for Updates") + box.setText(msg) + + if msg != "Already up to date": + # Add buttons + update_now_btn = box.addButton("Update Now", QMessageBox.AcceptRole) + later_btn = box.addButton("Update Later", QMessageBox.RejectRole) + else: + ok_btn = box.addButton("OK", QMessageBox.AcceptRole) + + box.exec_() + + if msg != "Already up to date": + if box.clickedButton() == update_now_btn: + pass + elif box.clickedButton() == later_btn: + pass + # elif loc == "FAQ": # pass diff --git a/src/osdag/update_version_check.py b/src/osdag/update_version_check.py index 8cab2fb53..ea31d1489 100644 --- a/src/osdag/update_version_check.py +++ b/src/osdag/update_version_check.py @@ -34,8 +34,6 @@ def notifi(self) -> str: """Compare current version with latest version and return update message.""" try: latest_version = self.fetch_latest_version().lstrip("v").replace("_", ".") - print(f"Latest version found: {latest_version}") - if latest_version == "not found": return "Could not determine latest version." @@ -43,8 +41,6 @@ def notifi(self) -> str: return ( f"Current version: {self.old_version}
" f"Latest version: {latest_version}
" - f'Update will be available ' - f'here' ) return "Already up to date" From 2a9bb291a3f97a5b40308eb6b6571a7d9593857b Mon Sep 17 00:00:00 2001 From: Zehen-249 <1hasanmehendi123@gmail.com> Date: Wed, 20 Aug 2025 21:35:17 +0530 Subject: [PATCH 03/11] fix minor bugs and inconsistency --- src/osdag/_version.py | 1 + src/osdag/osdagMainPage.py | 26 ++++++++++++------ src/osdag/update_version_check.py | 45 ++++++++++++++++--------------- 3 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/osdag/_version.py b/src/osdag/_version.py index ab6e815b2..7ae7b3376 100644 --- a/src/osdag/_version.py +++ b/src/osdag/_version.py @@ -1 +1,2 @@ __version__ = "2025.01.a.2" +__installation_type__ = "conda" \ No newline at end of file diff --git a/src/osdag/osdagMainPage.py b/src/osdag/osdagMainPage.py index 845d81da9..bf6125219 100644 --- a/src/osdag/osdagMainPage.py +++ b/src/osdag/osdagMainPage.py @@ -510,16 +510,19 @@ def selection_change(self): self.ask_question() elif loc == "Check for Update": update_class = Update() - msg = update_class.notifi() + try: + update_avl, msg = update_class.notifi() + except ConnectionError as e: + QMessageBox.critical(self, "Network Error", str(e)) + return # QMessageBox.information(self, 'Info',msg) box = QMessageBox(self) box.setIcon(QMessageBox.Information) - box.setWindowTitle("Check for Updates") + box.setWindowTitle("Update Status") box.setText(msg) - if msg != "Already up to date": - # Add buttons + if update_avl: update_now_btn = box.addButton("Update Now", QMessageBox.AcceptRole) later_btn = box.addButton("Update Later", QMessageBox.RejectRole) else: @@ -527,11 +530,18 @@ def selection_change(self): box.exec_() - if msg != "Already up to date": + if update_avl: if box.clickedButton() == update_now_btn: - pass - elif box.clickedButton() == later_btn: - pass + confirm_update = QMessageBox(self) + confirm_update.setIcon(QMessageBox.Information) + confirm_update.setWindowTitle("Confirm Update") + confirm_update.setText("This may take some time....\nDo you want to continue?") + confirm_update.setStandardButtons(QMessageBox.Yes | QMessageBox.No) + confirm_update.setDefaultButton(QMessageBox.No) + + result = confirm_update.exec_() + if result == QMessageBox.Yes: + pass # elif loc == "FAQ": # pass diff --git a/src/osdag/update_version_check.py b/src/osdag/update_version_check.py index ea31d1489..89704f0e9 100644 --- a/src/osdag/update_version_check.py +++ b/src/osdag/update_version_check.py @@ -3,15 +3,16 @@ import urllib.request import re from pathlib import Path +from packaging.version import Version, InvalidVersion version_file = Path(__file__).parent / "_version.py" -ns = {} -exec(version_file.read_text(), ns) -curr_version = ns["__version__"] +version_var = {} +exec(version_file.read_text(), version_var) +curr_version = version_var["__version__"] class Update(): URL = "https://osdag.fossee.in/resources/downloads" - PATTERN = re.compile(r'Install Osdag \((v[0-9a-zA-Z_]+)\)') + PATTERN = re.compile(r'Install\s+Osdag\s*\(\s*v([\w._-]+)\s*\)', re.IGNORECASE) def __init__(self): super().__init__() @@ -26,26 +27,28 @@ def fetch_latest_version(self) -> str: match = self.PATTERN.search(decoded_line) if match: return match.group(1) - return "not found" - except Exception as e: - raise ConnectionError(f"Error fetching latest version: {e}") + return None + except urllib.error.URLError as e: + raise ConnectionError(f"Network error: {e.reason}") + except urllib.error.HTTPError as e: + raise ConnectionError(f"HTTP error {e.code}: {e.reason}") def notifi(self) -> str: """Compare current version with latest version and return update message.""" try: - latest_version = self.fetch_latest_version().lstrip("v").replace("_", ".") - if latest_version == "not found": - return "Could not determine latest version." - - if latest_version != self.old_version: - return ( - f"Current version: {self.old_version}
" - f"Latest version: {latest_version}
" + latest_version = self.fetch_latest_version() + if latest_version is None: + return False, "Could not determine latest version." + + latest_version = latest_version.lstrip("v").replace("_", ".") + if Version(latest_version) > Version(self.old_version): + return True, ( + f"Current version: {self.old_version}\n" + f"Latest version: {latest_version}\n" ) - - return "Already up to date" - - except Exception as e: - return str(e) - + else: + return False, "Already up to date" + + except InvalidVersion: + return False, "Could not parse version string." From b4ff568a88ecf383932a39b47c2da6e9a46814e4 Mon Sep 17 00:00:00 2001 From: Zehen-249 <1hasanmehendi123@gmail.com> Date: Wed, 20 Aug 2025 22:36:43 +0530 Subject: [PATCH 04/11] add update_to_latest method to show dummy update --- src/osdag/_version.py | 2 +- src/osdag/osdagMainPage.py | 2 +- src/osdag/update_version_check.py | 32 ++++++++++++++++++++++++++++++- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/osdag/_version.py b/src/osdag/_version.py index 7ae7b3376..5e987e66b 100644 --- a/src/osdag/_version.py +++ b/src/osdag/_version.py @@ -1,2 +1,2 @@ -__version__ = "2025.01.a.2" +__version__ = "2025.01.a.1" __installation_type__ = "conda" \ No newline at end of file diff --git a/src/osdag/osdagMainPage.py b/src/osdag/osdagMainPage.py index bf6125219..9568a7014 100644 --- a/src/osdag/osdagMainPage.py +++ b/src/osdag/osdagMainPage.py @@ -541,7 +541,7 @@ def selection_change(self): result = confirm_update.exec_() if result == QMessageBox.Yes: - pass + print(update_class.update_to_latest()) # elif loc == "FAQ": # pass diff --git a/src/osdag/update_version_check.py b/src/osdag/update_version_check.py index 89704f0e9..968eb3472 100644 --- a/src/osdag/update_version_check.py +++ b/src/osdag/update_version_check.py @@ -4,19 +4,24 @@ import re from pathlib import Path from packaging.version import Version, InvalidVersion +from PyQt5.QtCore import QObject, QProcess, pyqtSignal +import subprocess + version_file = Path(__file__).parent / "_version.py" version_var = {} exec(version_file.read_text(), version_var) curr_version = version_var["__version__"] -class Update(): +class Update(QObject): + URL = "https://osdag.fossee.in/resources/downloads" PATTERN = re.compile(r'Install\s+Osdag\s*\(\s*v([\w._-]+)\s*\)', re.IGNORECASE) def __init__(self): super().__init__() self.old_version = curr_version + self.process = None def fetch_latest_version(self) -> str: """Fetch the latest version string from Osdag downloads page.""" @@ -51,4 +56,29 @@ def notifi(self) -> str: except InvalidVersion: return False, "Could not parse version string." + + def update_to_latest(self): + """Run conda update in background using QProcess.""" + try: + latest_version = self.fetch_latest_version() + if latest_version: + latest_version = latest_version.lstrip("v").replace("_", ".") + except Exception as e: + return + try: + # safer conda install + # cmd = ["conda", "install", "-y", f"osdag=={latest_version}"] + cmd = ["cmd", "/c", f"echo Updating to version {latest_version} && timeout /t 5"] + + result = subprocess.run(cmd, capture_output=False, text=True) + if result.returncode == 0: + return (True, "Update successful! Please restart Osdag.") + else: + return (False, f"Update failed.\nError: {result.stderr}\n" + "Please retry or run:\nconda install --force-reinstall osdag::osdag") + + + except Exception as e: + return (False, f"Update failed: {e}") + \ No newline at end of file From 8a01011c1cf736f45e3dc7d07950d2d28d179b07 Mon Sep 17 00:00:00 2001 From: Zehen-249 <1hasanmehendi123@gmail.com> Date: Wed, 20 Aug 2025 22:39:58 +0530 Subject: [PATCH 05/11] seperate pixi and conda based installation updates --- src/osdag/update_version_check.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/osdag/update_version_check.py b/src/osdag/update_version_check.py index 968eb3472..9105a68a1 100644 --- a/src/osdag/update_version_check.py +++ b/src/osdag/update_version_check.py @@ -12,7 +12,7 @@ version_var = {} exec(version_file.read_text(), version_var) curr_version = version_var["__version__"] - +install_type = version_var["__installation_type__"] class Update(QObject): URL = "https://osdag.fossee.in/resources/downloads" @@ -68,7 +68,10 @@ def update_to_latest(self): try: # safer conda install # cmd = ["conda", "install", "-y", f"osdag=={latest_version}"] - cmd = ["cmd", "/c", f"echo Updating to version {latest_version} && timeout /t 5"] + if install_type == "conda": + cmd = ["cmd", "/c", f"echo Updating to version {latest_version} with conda && timeout /t 5"] + elif install_type == "pixi": + cmd = ["cmd", "/c", f"echo Updating to version {latest_version} with pixi && timeout /t 5"] result = subprocess.run(cmd, capture_output=False, text=True) if result.returncode == 0: From 01d4950c2f541ea6d609350f52fdc744717a5600 Mon Sep 17 00:00:00 2001 From: Zehen-249 <1hasanmehendi123@gmail.com> Date: Wed, 20 Aug 2025 22:52:18 +0530 Subject: [PATCH 06/11] add synchronised update feature for conda(GUI block until update completes) --- src/osdag/osdagMainPage.py | 6 +++++- src/osdag/update_version_check.py | 6 +++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/osdag/osdagMainPage.py b/src/osdag/osdagMainPage.py index 9568a7014..a457f905e 100644 --- a/src/osdag/osdagMainPage.py +++ b/src/osdag/osdagMainPage.py @@ -541,7 +541,11 @@ def selection_change(self): result = confirm_update.exec_() if result == QMessageBox.Yes: - print(update_class.update_to_latest()) + update_status, msg = update_class.update_to_latest() + if update_status: + QMessageBox.information(self, "Update", msg) + else: + QMessageBox.warning(self, "Update Failed", msg) # elif loc == "FAQ": # pass diff --git a/src/osdag/update_version_check.py b/src/osdag/update_version_check.py index 9105a68a1..c4a6e6d29 100644 --- a/src/osdag/update_version_check.py +++ b/src/osdag/update_version_check.py @@ -66,12 +66,12 @@ def update_to_latest(self): except Exception as e: return try: - # safer conda install # cmd = ["conda", "install", "-y", f"osdag=={latest_version}"] if install_type == "conda": - cmd = ["cmd", "/c", f"echo Updating to version {latest_version} with conda && timeout /t 5"] + cmd = ["conda", "update", "-y", f"osdag"] + # cmd = ["cmd", "/c", f"echo Updating to version {latest_version} with conda && timeout /t 5"] elif install_type == "pixi": - cmd = ["cmd", "/c", f"echo Updating to version {latest_version} with pixi && timeout /t 5"] + cmd = ["pixi", "update", "-y", f"osdag"] result = subprocess.run(cmd, capture_output=False, text=True) if result.returncode == 0: From 4467ef51161bff5a0e53cb812346d37ecfacbd91 Mon Sep 17 00:00:00 2001 From: Zehen-249 <1hasanmehendi123@gmail.com> Date: Wed, 20 Aug 2025 23:11:04 +0530 Subject: [PATCH 07/11] Add conda.exe and pixi.exe executable paths --- src/osdag/update_version_check.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/src/osdag/update_version_check.py b/src/osdag/update_version_check.py index c4a6e6d29..9309fb9b9 100644 --- a/src/osdag/update_version_check.py +++ b/src/osdag/update_version_check.py @@ -6,6 +6,7 @@ from packaging.version import Version, InvalidVersion from PyQt5.QtCore import QObject, QProcess, pyqtSignal import subprocess +import sys, os version_file = Path(__file__).parent / "_version.py" @@ -67,11 +68,26 @@ def update_to_latest(self): return try: # cmd = ["conda", "install", "-y", f"osdag=={latest_version}"] + + env_path = sys.prefix + env_path = Path(env_path) + base_conda_path = env_path.parents[1] + if sys.platform.startswith("win"): + conda_path = base_conda_path / "Scripts" / "conda.exe" + pixi_path = env_path / "Scripts" / "pixi.exe" + print(conda_path, pixi_path) + else: + conda_path = base_conda_path / "bin/conda" + pixi_path = env_path / "bin/pixi" if install_type == "conda": - cmd = ["conda", "update", "-y", f"osdag"] - # cmd = ["cmd", "/c", f"echo Updating to version {latest_version} with conda && timeout /t 5"] + if not conda_path.exists(): + return False, f"conda.exe not found in {conda_path}" + # cmd = [str(conda_path), "update", "-y", f"osdag"] + cmd = ["cmd", "/c", f"echo Updating to version {latest_version} with conda && timeout /t 5"] elif install_type == "pixi": - cmd = ["pixi", "update", "-y", f"osdag"] + if not pixi_path.exists(): + return False, f"pixi.exe not found in {pixi_path}" + cmd = [str(pixi_path), "update", "-y", f"osdag"] result = subprocess.run(cmd, capture_output=False, text=True) if result.returncode == 0: From 0a3841fef3a1325bf4f93ff403d0f304802e0760 Mon Sep 17 00:00:00 2001 From: Zehen-249 <1hasanmehendi123@gmail.com> Date: Thu, 21 Aug 2025 10:08:12 +0530 Subject: [PATCH 08/11] Add asyn update feature using QProcess --- src/osdag/osdagMainPage.py | 26 ++++++++---- src/osdag/update_version_check.py | 68 ++++++++++++++++++++++++------- 2 files changed, 71 insertions(+), 23 deletions(-) diff --git a/src/osdag/osdagMainPage.py b/src/osdag/osdagMainPage.py index a457f905e..af811af53 100644 --- a/src/osdag/osdagMainPage.py +++ b/src/osdag/osdagMainPage.py @@ -509,9 +509,9 @@ def selection_change(self): elif loc == "Ask Us a Question": self.ask_question() elif loc == "Check for Update": - update_class = Update() + self.updater = Update() try: - update_avl, msg = update_class.notifi() + update_avl, msg = self.updater.notifi() except ConnectionError as e: QMessageBox.critical(self, "Network Error", str(e)) return @@ -530,8 +530,7 @@ def selection_change(self): box.exec_() - if update_avl: - if box.clickedButton() == update_now_btn: + if update_avl and box.clickedButton() == update_now_btn: confirm_update = QMessageBox(self) confirm_update.setIcon(QMessageBox.Information) confirm_update.setWindowTitle("Confirm Update") @@ -541,15 +540,24 @@ def selection_change(self): result = confirm_update.exec_() if result == QMessageBox.Yes: - update_status, msg = update_class.update_to_latest() - if update_status: - QMessageBox.information(self, "Update", msg) - else: - QMessageBox.warning(self, "Update Failed", msg) + + self.updater.output_signal.connect(lambda text: print(text, end="")) + self.updater.finished_signal.connect(self.handle_update_finished) + self.updater.update_to_latest() + # if update_status: + # QMessageBox.information(self, "Update", msg) + # else: + # QMessageBox.warning(self, "Update Failed", msg) + # elif loc == "FAQ": # pass + def handle_update_finished(self,success, msg): + if success: + QMessageBox.information(self, "Update", msg) + else: + QMessageBox.warning(self, "Update Failed", msg) def select_workspace_folder(self): # This function prompts the user to select the workspace folder and returns the name of the workspace folder diff --git a/src/osdag/update_version_check.py b/src/osdag/update_version_check.py index 9309fb9b9..2494f7e08 100644 --- a/src/osdag/update_version_check.py +++ b/src/osdag/update_version_check.py @@ -15,6 +15,8 @@ curr_version = version_var["__version__"] install_type = version_var["__installation_type__"] class Update(QObject): + output_signal = pyqtSignal(str) + finished_signal = pyqtSignal(bool, str) URL = "https://osdag.fossee.in/resources/downloads" PATTERN = re.compile(r'Install\s+Osdag\s*\(\s*v([\w._-]+)\s*\)', re.IGNORECASE) @@ -22,7 +24,7 @@ class Update(QObject): def __init__(self): super().__init__() self.old_version = curr_version - self.process = None + self.process = QProcess(self) def fetch_latest_version(self) -> str: """Fetch the latest version string from Osdag downloads page.""" @@ -81,23 +83,61 @@ def update_to_latest(self): pixi_path = env_path / "bin/pixi" if install_type == "conda": if not conda_path.exists(): - return False, f"conda.exe not found in {conda_path}" - # cmd = [str(conda_path), "update", "-y", f"osdag"] - cmd = ["cmd", "/c", f"echo Updating to version {latest_version} with conda && timeout /t 5"] + self.finished_signal.emit(False, f"conda not found at {conda_path}") + return + cmd = [str(conda_path), "update", "-y", "osdag"] + # cmd = ["cmd", "/c", f"echo Updating to version {latest_version} with conda && timeout /t 5"] elif install_type == "pixi": if not pixi_path.exists(): - return False, f"pixi.exe not found in {pixi_path}" - cmd = [str(pixi_path), "update", "-y", f"osdag"] + self.finished_signal.emit(False, f"pixi.exe not found in {pixi_path}") + return + cmd = [str(pixi_path), "update", "-y", "osdag"] - result = subprocess.run(cmd, capture_output=False, text=True) - if result.returncode == 0: - return (True, "Update successful! Please restart Osdag.") - else: - return (False, f"Update failed.\nError: {result.stderr}\n" - "Please retry or run:\nconda install --force-reinstall osdag::osdag") + # Create QProcess + self.process.setProgram(cmd[0]) + self.process.setArguments(cmd[1:]) + + # Connect signals for output + self.process.readyReadStandardOutput.connect(self.handle_stdout) + self.process.readyReadStandardError.connect(self.handle_stderr) + self.process.finished.connect(self.handle_finished) + + # Start the process + self.process.start() + + # result = subprocess.run(cmd, capture_output=False, text=True) + # if result.returncode == 0: + # self.finished_signal.emit(True, "Update successful! Please restart Osdag.") + # else: + # self.finished_signal.emit(False, f"Update failed.\nError: {result.stderr}\n" + # "Please retry or run:\nconda install --force-reinstall osdag::osdag") except Exception as e: - return (False, f"Update failed: {e}") + self.finished_signal.emit(False, f"Update failed: {e}") + return + + + def handle_stdout(self): + """Read stdout and print it, also emit signal for GUI.""" + if self.process: + output = self.process.readAllStandardOutput().data().decode() + print(output, end="") + self.output_signal.emit(output) + + def handle_stderr(self): + """Read stderr and print it.""" + if self.process: + error = self.process.readAllStandardError().data().decode() + print(error, end="") + self.output_signal.emit(error) + + def handle_finished(self, exit_code, exit_status): + """Handle when the process finishes.""" + if exit_code == 0: + self.finished_signal.emit(True, "Update successful! Please restart Osdag.") + else: + self.finished_signal.emit(False, f"Update failed with code {exit_code}") + self.process = None - \ No newline at end of file + \ No newline at end of file From 04ea46c4478bc9f24a0486136c978b8018755a41 Mon Sep 17 00:00:00 2001 From: Zehen-249 <1hasanmehendi123@gmail.com> Date: Thu, 21 Aug 2025 10:36:26 +0530 Subject: [PATCH 09/11] Add dialog box to show update status --- src/osdag/osdagMainPage.py | 21 ++++++++++++++++++--- src/osdag/update_version_check.py | 19 +++++++++++++++---- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/src/osdag/osdagMainPage.py b/src/osdag/osdagMainPage.py index af811af53..da6a4475c 100644 --- a/src/osdag/osdagMainPage.py +++ b/src/osdag/osdagMainPage.py @@ -88,7 +88,7 @@ import time from importlib.resources import files import urllib.request -from PyQt5.QtWidgets import QMessageBox,QApplication, QDialog, QMainWindow +from PyQt5.QtWidgets import QMessageBox,QApplication, QDialog, QMainWindow, QTextEdit from .update_version_check import Update #from Thread import timer from .get_DPI_scale import scale @@ -540,8 +540,22 @@ def selection_change(self): result = confirm_update.exec_() if result == QMessageBox.Yes: - - self.updater.output_signal.connect(lambda text: print(text, end="")) + self.progress_dialog = QDialog(self) + self.progress_dialog.setWindowTitle("Updating Osdag") + layout = QVBoxLayout() + + self.progress_label = QLabel("Updating, please wait...") + layout.addWidget(self.progress_label) + + self.progress_text = QTextEdit() + self.progress_text.setReadOnly(True) + layout.addWidget(self.progress_text) + + self.progress_dialog.setLayout(layout) + self.progress_dialog.setModal(True) + self.progress_dialog.show() + + self.updater.output_signal.connect(lambda text: self.progress_text.append(text)) self.updater.finished_signal.connect(self.handle_update_finished) self.updater.update_to_latest() # if update_status: @@ -554,6 +568,7 @@ def selection_change(self): # pass def handle_update_finished(self,success, msg): + self.progress_dialog.close() if success: QMessageBox.information(self, "Update", msg) else: diff --git a/src/osdag/update_version_check.py b/src/osdag/update_version_check.py index 2494f7e08..46baeaa57 100644 --- a/src/osdag/update_version_check.py +++ b/src/osdag/update_version_check.py @@ -5,6 +5,7 @@ from pathlib import Path from packaging.version import Version, InvalidVersion from PyQt5.QtCore import QObject, QProcess, pyqtSignal +from PyQt5.QtWidgets import QDialog, QTextEdit, QLabel import subprocess import sys, os @@ -18,6 +19,7 @@ class Update(QObject): output_signal = pyqtSignal(str) finished_signal = pyqtSignal(bool, str) + URL = "https://osdag.fossee.in/resources/downloads" PATTERN = re.compile(r'Install\s+Osdag\s*\(\s*v([\w._-]+)\s*\)', re.IGNORECASE) @@ -121,16 +123,25 @@ def update_to_latest(self): def handle_stdout(self): """Read stdout and print it, also emit signal for GUI.""" if self.process: - output = self.process.readAllStandardOutput().data().decode() - print(output, end="") + output = self.process.readAllStandardOutput().data().decode("utf-8") self.output_signal.emit(output) + print(output, end="") + # self.progress_text.append(output) + # self.progress_text.verticalScrollBar().setValue( + # self.progress_text.verticalScrollBar().maximum() + # ) def handle_stderr(self): """Read stderr and print it.""" if self.process: - error = self.process.readAllStandardError().data().decode() - print(error, end="") + error = self.process.readAllStandardError().data().decode("utf-8") self.output_signal.emit(error) + print(error, end="") + # self.output_signal.emit(error) + # self.progress_text.append(error) + # self.progress_text.verticalScrollBar().setValue( + # self.progress_text.verticalScrollBar().maximum() + # ) def handle_finished(self, exit_code, exit_status): """Handle when the process finishes.""" From 1fab443269f3384c58b20a01194a8b52ca6cadce Mon Sep 17 00:00:00 2001 From: Zehen-249 <1hasanmehendi123@gmail.com> Date: Thu, 21 Aug 2025 10:44:17 +0530 Subject: [PATCH 10/11] Make update status box Non-modal --- src/osdag/osdagMainPage.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/osdag/osdagMainPage.py b/src/osdag/osdagMainPage.py index da6a4475c..b36491701 100644 --- a/src/osdag/osdagMainPage.py +++ b/src/osdag/osdagMainPage.py @@ -552,7 +552,8 @@ def selection_change(self): layout.addWidget(self.progress_text) self.progress_dialog.setLayout(layout) - self.progress_dialog.setModal(True) + self.progress_dialog.setModal(False) + self.progress_dialog.setWindowFlags(self.progress_dialog.windowFlags() | Qt.WindowStaysOnTopHint) self.progress_dialog.show() self.updater.output_signal.connect(lambda text: self.progress_text.append(text)) From e1778c2416e09af2929cd195f2de5dd0788dcedf Mon Sep 17 00:00:00 2001 From: Zehen-249 <1hasanmehendi123@gmail.com> Date: Wed, 10 Sep 2025 18:31:21 +0530 Subject: [PATCH 11/11] Use conda channel and pixi to fetch latest versions --- src/osdag/osdagMainPage.py | 7 ++- src/osdag/update_version_check.py | 89 ++++++++++++++++++------------- 2 files changed, 57 insertions(+), 39 deletions(-) diff --git a/src/osdag/osdagMainPage.py b/src/osdag/osdagMainPage.py index b36491701..d1be6d56f 100644 --- a/src/osdag/osdagMainPage.py +++ b/src/osdag/osdagMainPage.py @@ -549,10 +549,13 @@ def selection_change(self): self.progress_text = QTextEdit() self.progress_text.setReadOnly(True) + self.progress_text.verticalScrollBar().setValue( + self.progress_text.verticalScrollBar().maximum() + ) layout.addWidget(self.progress_text) self.progress_dialog.setLayout(layout) - self.progress_dialog.setModal(False) + self.progress_dialog.setModal(True) self.progress_dialog.setWindowFlags(self.progress_dialog.windowFlags() | Qt.WindowStaysOnTopHint) self.progress_dialog.show() @@ -571,7 +574,7 @@ def selection_change(self): def handle_update_finished(self,success, msg): self.progress_dialog.close() if success: - QMessageBox.information(self, "Update", msg) + QMessageBox.information(self, "Update Completed", msg) else: QMessageBox.warning(self, "Update Failed", msg) diff --git a/src/osdag/update_version_check.py b/src/osdag/update_version_check.py index 46baeaa57..3f9184692 100644 --- a/src/osdag/update_version_check.py +++ b/src/osdag/update_version_check.py @@ -8,6 +8,7 @@ from PyQt5.QtWidgets import QDialog, QTextEdit, QLabel import subprocess import sys, os +import json version_file = Path(__file__).parent / "_version.py" @@ -15,33 +16,60 @@ exec(version_file.read_text(), version_var) curr_version = version_var["__version__"] install_type = version_var["__installation_type__"] + + + class Update(QObject): output_signal = pyqtSignal(str) finished_signal = pyqtSignal(bool, str) - - URL = "https://osdag.fossee.in/resources/downloads" - PATTERN = re.compile(r'Install\s+Osdag\s*\(\s*v([\w._-]+)\s*\)', re.IGNORECASE) - def __init__(self): super().__init__() self.old_version = curr_version self.process = QProcess(self) + + def _set_exec_paths(self): + env_path = sys.prefix + env_path = Path(env_path) + base_conda_path = env_path.parents[1] + if sys.platform.startswith("win"): + conda_path = base_conda_path / "Scripts" / "conda.exe" + pixi_path = env_path / "Scripts" / "pixi.exe" + else: + conda_path = base_conda_path / "bin/conda" + pixi_path = env_path / "bin/pixi" + + self.conda_path = str(conda_path) + self.pixi_path = str(pixi_path) def fetch_latest_version(self) -> str: """Fetch the latest version string from Osdag downloads page.""" - try: - with urllib.request.urlopen(self.URL) as response: - for line in response: - decoded_line = line.decode("utf-8") - match = self.PATTERN.search(decoded_line) - if match: - return match.group(1) - return None - except urllib.error.URLError as e: - raise ConnectionError(f"Network error: {e.reason}") - except urllib.error.HTTPError as e: - raise ConnectionError(f"HTTP error {e.code}: {e.reason}") + self._set_exec_paths() + if install_type == "conda": + cmd = [self.conda_path, "search", "-c", "conda-forge", "osdag::osdag", "--info", "--json"] + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + data = json.loads(result.stdout) + except subprocess.CalledProcessError as e: + raise ConnectionError(f"Failed to fetch version info: {e.stderr}") + if data: + versions = data.get("osdag") + if versions: + return sorted(versions, key=lambda x:x["version"])[-1]["version"] + else: return None + + elif install_type == "pixi": + cmd = [self.pixi_path, "search", "osdag", "--channel", "osdag"] + try: + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + for line in result.stdout.splitlines(): + if line.strip().startswith("Version"): + return line.split()[-1].strip() + + else: return None + + except subprocess.CalledProcessError as e: + raise ConnectionError(f"Failed to fetch version info: {e.stderr}") def notifi(self) -> str: """Compare current version with latest version and return update message.""" @@ -73,27 +101,16 @@ def update_to_latest(self): try: # cmd = ["conda", "install", "-y", f"osdag=={latest_version}"] - env_path = sys.prefix - env_path = Path(env_path) - base_conda_path = env_path.parents[1] - if sys.platform.startswith("win"): - conda_path = base_conda_path / "Scripts" / "conda.exe" - pixi_path = env_path / "Scripts" / "pixi.exe" - print(conda_path, pixi_path) - else: - conda_path = base_conda_path / "bin/conda" - pixi_path = env_path / "bin/pixi" if install_type == "conda": - if not conda_path.exists(): - self.finished_signal.emit(False, f"conda not found at {conda_path}") - return - cmd = [str(conda_path), "update", "-y", "osdag"] - # cmd = ["cmd", "/c", f"echo Updating to version {latest_version} with conda && timeout /t 5"] + if not Path(self.conda_path).exists(): + self.finished_signal.emit(False, f"conda not found at {self.conda_path}") + return + cmd = [self.conda_path, "update", "-y", "osdag", "--channel", "osdag"] elif install_type == "pixi": - if not pixi_path.exists(): - self.finished_signal.emit(False, f"pixi.exe not found in {pixi_path}") - return - cmd = [str(pixi_path), "update", "-y", "osdag"] + if not Path(self.pixi_path).exists(): + self.finished_signal.emit(False, f"pixi not found at {self.pixi_path}") + return + cmd = [self.pixi_path, "update", "-y", "osdag", "--channel", "osdag"] # Create QProcess self.process.setProgram(cmd[0]) @@ -150,5 +167,3 @@ def handle_finished(self, exit_code, exit_status): else: self.finished_signal.emit(False, f"Update failed with code {exit_code}") self.process = None - - \ No newline at end of file