From bd143feae20f97203e9d0526a46706da1150326e Mon Sep 17 00:00:00 2001 From: Yorik van Havre Date: Tue, 14 May 2024 11:08:16 +0200 Subject: [PATCH] BIM: Added ifcopenshell update command --- src/Mod/BIM/CMakeLists.txt | 1 + src/Mod/BIM/nativeifc/ifc_commands.py | 3 +- src/Mod/BIM/nativeifc/ifc_openshell.py | 182 +++++++++++++++++++++++++ 3 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 src/Mod/BIM/nativeifc/ifc_openshell.py diff --git a/src/Mod/BIM/CMakeLists.txt b/src/Mod/BIM/CMakeLists.txt index 156f77fee3..d9e3cf3665 100644 --- a/src/Mod/BIM/CMakeLists.txt +++ b/src/Mod/BIM/CMakeLists.txt @@ -186,6 +186,7 @@ SET(nativeifc_SRCS nativeifc/ifc_tree.py nativeifc/ifc_viewproviders.py nativeifc/__init__.py + nativeifc/ifc_openshell.py ) SOURCE_GROUP("" FILES ${Arch_SRCS}) diff --git a/src/Mod/BIM/nativeifc/ifc_commands.py b/src/Mod/BIM/nativeifc/ifc_commands.py index 404ebe0123..4e49ea3ed9 100644 --- a/src/Mod/BIM/nativeifc/ifc_commands.py +++ b/src/Mod/BIM/nativeifc/ifc_commands.py @@ -25,6 +25,7 @@ import FreeCAD import FreeCADGui +from nativeifc import ifc_openshell translate = FreeCAD.Qt.translate QT_TRANSLATE_NOOP = FreeCAD.Qt.QT_TRANSLATE_NOOP @@ -243,7 +244,7 @@ class IFC_SaveAs: def get_commands(): """Returns a list of IFC commands""" - return ["IFC_Diff", "IFC_Expand", "IFC_MakeProject"] + return ["IFC_Diff", "IFC_Expand", "IFC_MakeProject", "IFC_UpdateIOS"] # initialize commands diff --git a/src/Mod/BIM/nativeifc/ifc_openshell.py b/src/Mod/BIM/nativeifc/ifc_openshell.py new file mode 100644 index 0000000000..f576731d0b --- /dev/null +++ b/src/Mod/BIM/nativeifc/ifc_openshell.py @@ -0,0 +1,182 @@ +# -*- 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 = tranlate("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