# -*- coding: utf8 -*- # *************************************************************************** # * * # * Copyright (c) 2024 Yorik van Havre * # * * # * This program is free software; you can redistribute it and/or modify * # * it under the terms of the GNU Lesser General Public License (LGPL) * # * as published by the Free Software Foundation; either version 2 of * # * the License, or (at your option) any later version. * # * for detail see the LICENCE text file. * # * * # * This program is distributed in the hope that it will be useful, * # * but WITHOUT ANY WARRANTY; without even the implied warranty of * # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * # * GNU Library General Public License for more details. * # * * # * You should have received a copy of the GNU Library General Public * # * License along with this program; if not, write to the Free Software * # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * # * USA * # * * # *************************************************************************** """Utilities to help people verify and update their version of ifcopenshell""" import FreeCAD import FreeCADGui translate = FreeCAD.Qt.translate QT_TRANSLATE_NOOP = FreeCAD.Qt.QT_TRANSLATE_NOOP class IFC_UpdateIOS: """Shows a dialog to update IfcOpenShell""" def GetResources(self): tt = QT_TRANSLATE_NOOP("IFC_UpdateIOS", "Shows a dialog to update IfcOpenShell") return { "Pixmap": "IFC", "MenuText": QT_TRANSLATE_NOOP("IFC_UpdateIOS", "IfcOpenShell update"), "ToolTip": tt, } def Activated(self): """Shows the updater UI""" version = self.get_current_version() avail = self.get_avail_version() if version: comp = self.compare_versions(avail, version) if comp > 0: self.show_dialog("update", avail) else: self.show_dialog("uptodate") else: self.show_dialog("install", avail) def show_dialog(self, mode, version=None): """Shows a dialog to the user""" from PySide import QtGui title = translate("BIM", "IfcOpenShell update") note = translate("BIM", "The update is installed in your FreeCAD's user directory and won't affect the rest of your system.") if mode == "update": text = translate("BIM", "An update to your installed IfcOpenShell version is available") text += ": " + version + ". " text += translate("BIM", "Would you like to install that update?") text += " " + note buttons = QtGui.QMessageBox.Cancel | QtGui.QMessageBox.Ok elif mode == "uptodate": text = translate("BIM", "Your version of IfcOpenShell is already up to date") buttons = QtGui.QMessageBox.Ok elif mode == "install": text = translate("BIM", "No existing IfcOpenShell installation found on this system.") text += " " text += translate("BIM", "Would you like to install the most recent version?") text += " (" + version + ") " + note buttons = QtGui.QMessageBox.Cancel | QtGui.QMessageBox.Ok reply = QtGui.QMessageBox.information(None, title, text, buttons) if reply == QtGui.QMessageBox.Ok: if mode in ["update", "install"]: result = self.install() text = translate("BIM", "IfcOpenShell update successfully installed.") buttons = QtGui.QMessageBox.Ok reply = QtGui.QMessageBox.information(None, title, text, buttons) def install(self): """Installs the given version""" import addonmanager_utilities as utils from PySide import QtCore, QtGui QtGui.QApplication.setOverrideCursor(QtCore.Qt.WaitCursor) vendor_path = utils.get_pip_target_directory() args = ["install", "--disable-pip-version-check", "--target", vendor_path, "ifcopenshell"] result = self.run_pip(args) QtGui.QApplication.restoreOverrideCursor() return result def run_pip(self, args): """Runs a pip command""" import addonmanager_utilities as utils import freecad.utils cmd = [freecad.utils.get_python_exe(), "-m", "pip"] cmd.extend(args) return utils.run_interruptable_subprocess(cmd) def get_current_version(self): """Retrieves the current ifcopenshell version""" import addonmanager_utilities as utils try: import ifcopenshell version = ifcopenshell.version except: version = "" if version.startswith("v"): # this is a pip version vendor_path = utils.get_pip_target_directory() result = self.run_pip(["list", "--path", vendor_path]) result = result.stdout.split() if "ifcopenshell" in result: version = result[result.index("ifcopenshell")+1] return version def get_avail_version(self): """Retrieves an available ifcopenshell version""" result = self.run_pip(["index", "versions", "ifcopenshell"]) result = result.stdout.split() result = result[result.index("versions:")+1:] result = [r.strip(",") for r in result] return result[0] # we return the biggest def compare_versions(self, v1, v2): """Compare two version strings in the form '0.7.0' """ # code from https://www.geeksforgeeks.org/compare-two-version-numbers arr1 = v1.split(".") arr2 = v2.split(".") n = len(arr1) m = len(arr2) arr1 = [int(i) for i in arr1] arr2 = [int(i) for i in arr2] if n>m: for i in range(m, n): arr2.append(0) elif m>n: for i in range(n, m): arr1.append(0) for i in range(len(arr1)): if arr1[i] > arr2[i]: return 1 elif arr2[i] > arr1[i]: return -1 return 0 FreeCADGui.addCommand("IFC_UpdateIOS", IFC_UpdateIOS()) # >>> utils.get_pip_target_directory() # '/home/yorik/.local/share/FreeCAD/Mod/../AdditionalPythonPackages/py311' # >>> import freecad.utils # >>> freecad.utils # # >>> freecad.utils.get_python_exe # # >>> freecad.utils.get_python_exe() # '/usr/bin/python3' # ... # >>> run_pip(["index", "versions", "ifcopenshell"]) # CompletedProcess(args=['/usr/bin/python3', '-m', 'pip', 'index', 'versions', 'ifcopenshell'], returncode=0, stdout='ifcopenshell (0.7.0.240423)\nAvailable versions: 0.7.0.240423, 0.7.0.240418, 0.7.0.240406\n', stderr='WARNING: pip index is currently an experimental command. It may be removed/changed in a future release without prior warning.\n') # pip install --disable-pip-version-check --target vendor_path ifcopenshell