Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FEAT: Toolkit installer #4593

Merged
merged 57 commits into from
May 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
f339849
Fix toolkit installer
Samuelopez-ansys Mar 11, 2024
827088b
Add tkinter ui for toolkits manager
Samuelopez-ansys Mar 12, 2024
b1d4ab9
Create virtual environment for new toolkits
Samuelopez-ansys Mar 12, 2024
5eb2719
Create virtual environment for new toolkits
Samuelopez-ansys Mar 12, 2024
68c95d8
Virtual environment pointed to AEDT ribbon
Samuelopez-ansys Mar 13, 2024
da2af27
Uninstall toolkit option
Samuelopez-ansys Mar 14, 2024
0722ce8
Wheelhouse
Samuelopez-ansys Mar 14, 2024
5a44bd8
Merge branch 'main' into feat/toolkit_installer
Samuelopez-ansys Mar 14, 2024
09145bb
Add button
Samuelopez-ansys Mar 15, 2024
047ab5c
Add button in run script
Samuelopez-ansys Mar 15, 2024
cd0c281
Merge branch 'main' into feat/toolkit_installer
Samuelopez-ansys Apr 22, 2024
ff83282
Fix toolkit manager
Samuelopez-ansys Apr 23, 2024
80d5f01
Install wheelhouse
Samuelopez-ansys Apr 23, 2024
ca66ccc
Merge branch 'main' into feat/toolkit_installer
Samuelopez-ansys Apr 23, 2024
35631e4
Fix venv issue
Samuelopez-ansys Apr 26, 2024
3f68e63
Change PyAEDT icons
Samuelopez-ansys Apr 26, 2024
943af54
Merge branch 'main' into feat/toolkit_installer
Samuelopez-ansys Apr 26, 2024
233fa78
Fix Codacy
Samuelopez-ansys Apr 26, 2024
10fd91c
Fix UT
Samuelopez-ansys Apr 26, 2024
141bf22
Merge branch 'main' into feat/toolkit_installer
Samuelopez-ansys Apr 26, 2024
1117397
Add PyAEDT Console
Samuelopez-ansys Apr 29, 2024
8d828ef
Add suggestion
Samuelopez-ansys Apr 29, 2024
63b7c9a
Add suggestion
Samuelopez-ansys Apr 29, 2024
0b958c4
Apply suggestions from code review
Samuelopez-ansys Apr 29, 2024
b0353c6
Merge remote-tracking branch 'origin/feat/toolkit_installer' into fea…
Samuelopez-ansys Apr 29, 2024
9a7813d
Merge remote-tracking branch 'origin/main' into feat/toolkit_installer
Samuelopez-ansys Apr 29, 2024
ea5463e
Add TK and TCK env variables in Linux
Samuelopez-ansys Apr 29, 2024
10f381c
Add TK and TCK env variables in Linux
Samuelopez-ansys Apr 29, 2024
e878fb2
Fix Linux logo
Samuelopez-ansys Apr 29, 2024
e881388
Linux compatibility
Samuelopez-ansys Apr 29, 2024
5e82308
Fix Maxwell name
Samuelopez-ansys Apr 29, 2024
2c33572
Workflows directory
Samuelopez-ansys Apr 30, 2024
665fdff
Workflows directory
Samuelopez-ansys Apr 30, 2024
ed834a9
Remove old scripts
Samuelopez-ansys Apr 30, 2024
2d6e830
Remove old scripts
Samuelopez-ansys Apr 30, 2024
dcb6826
Remove toolkit manager env
Samuelopez-ansys Apr 30, 2024
9f3b822
Install custom toolkit
Samuelopez-ansys Apr 30, 2024
61ae42d
Merge branch 'main' into feat/toolkit_installer
Samuelopez-ansys Apr 30, 2024
67f9176
Install custom toolkit
Samuelopez-ansys May 3, 2024
fda0be7
Install toolkit with pip
Samuelopez-ansys May 3, 2024
890a14c
Fix UT
Samuelopez-ansys May 3, 2024
8553172
Fix Codacy
Samuelopez-ansys May 3, 2024
e429f04
Fix Codacy
Samuelopez-ansys May 3, 2024
baa50a3
Merge branch 'main' into feat/toolkit_installer
Samuelopez-ansys May 3, 2024
40ed676
Added examples to Workflows
May 3, 2024
b30ad55
Added examples to Workflows
May 3, 2024
9214d78
Fix Codacy
Samuelopez-ansys May 3, 2024
5334c79
Merge remote-tracking branch 'origin/feat/toolkit_installer' into fea…
Samuelopez-ansys May 3, 2024
9e3a1f9
Separate installer and project
Samuelopez-ansys May 3, 2024
b16a6d0
Add workflows as simple scripts
Samuelopez-ansys May 3, 2024
563bc82
Add workflows as simple scripts
Samuelopez-ansys May 3, 2024
a2b7d65
Add simplorer
Samuelopez-ansys May 3, 2024
2ff6af1
Merge branch 'main' into feat/toolkit_installer
Samuelopez-ansys May 3, 2024
6cb453d
Fix Linux issues
Samuelopez-ansys May 3, 2024
554d9a0
Merge branch 'main' into feat/toolkit_installer
Samuelopez-ansys May 3, 2024
2fbbdc7
Added import_nastran to Workflows
May 3, 2024
b16e811
Merge branch 'main' into feat/toolkit_installer
Samuelopez-ansys May 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions _unittest/test_01_Design.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from pyaedt.application.design_solutions import model_names
from pyaedt.generic.general_methods import is_linux
from pyaedt.generic.general_methods import settings
from pyaedt.workflows import customize_automation_tab

test_subfolder = "T01"
if config["desktopVersion"] > "2022.2":
Expand Down Expand Up @@ -398,17 +399,18 @@ def test_36_test_load(self, add_app):
assert True

def test_37_add_custom_toolkit(self, desktop):
assert desktop.get_available_toolkits()
assert customize_automation_tab.available_toolkits

def test_38_toolkit(self, desktop):
file = os.path.join(self.local_scratch.path, "test.py")
with open(file, "w") as f:
f.write("import pyaedt\n")
assert desktop.add_script_to_menu(
"test_toolkit",
file,
assert customize_automation_tab.add_script_to_menu(
desktop_object=self.aedtapp.desktop_class, name="test_toolkit", script_file=file
)
assert customize_automation_tab.remove_script_from_menu(
desktop_object=self.aedtapp.desktop_class, name="test_toolkit"
)
assert desktop.remove_script_from_menu("test_toolkit")

def test_39_load_project(self, desktop):
new_project = os.path.join(self.local_scratch.path, "new.aedt")
Expand Down
34 changes: 18 additions & 16 deletions _unittest/test_01_toolkit_icons.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import os
import xml.etree.ElementTree as ET

import defusedxml.ElementTree as ET
import defusedxml.minidom

defusedxml.defuse_stdlib()


import pytest

from pyaedt.misc.aedtlib_personalib_install import write_tab_config
from pyaedt.workflows.customize_automation_tab import add_automation_tab


@pytest.fixture(scope="module", autouse=True)
Expand All @@ -17,8 +22,7 @@ def init(self, local_scratch):
self.local_scratch = local_scratch

def test_00_write_new_xml(self):
file_path = os.path.join(self.local_scratch.path, "TabConfig.xml")
write_tab_config(os.path.dirname(file_path), self.local_scratch.path)
file_path = add_automation_tab(name="Test", lib_dir=self.local_scratch.path)
root = self.validate_file_exists_and_pyaedt_tabs_added(file_path)
panels = root.findall("./panel")
panel_names = [panel.attrib["label"] for panel in panels]
Expand All @@ -29,7 +33,7 @@ def test_01_add_pyaedt_config_to_existing_existing_xml(self):
First write a dummy XML with a different Panel and then add PyAEDT's tabs
:return:
"""
file_path = os.path.join(self.local_scratch.path, "TabConfig.xml")
file_path = os.path.join(self.local_scratch.path, "Project", "TabConfig.xml")
with open(file_path, "w") as fid:
fid.write(
"""<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
Expand All @@ -47,15 +51,15 @@ def test_01_add_pyaedt_config_to_existing_existing_xml(self):
"""
)

write_tab_config(os.path.dirname(file_path), self.local_scratch.path)
file_path = add_automation_tab(name="Test", lib_dir=self.local_scratch.path)
root = self.validate_file_exists_and_pyaedt_tabs_added(file_path)
panels = root.findall("./panel")
panel_names = [panel.attrib["label"] for panel in panels]
assert len(panel_names) == 2
assert "Panel_1" in panel_names

def test_03_overwrite_existing_pyaedt_config(self):
file_path = os.path.join(self.local_scratch.path, "TabConfig.xml")
file_path = os.path.join(self.local_scratch.path, "Project", "TabConfig.xml")
with open(file_path, "w") as fid:
fid.write(
"""<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
Expand All @@ -72,14 +76,14 @@ def test_03_overwrite_existing_pyaedt_config(self):
</TabConfig>
"""
)
write_tab_config(os.path.dirname(file_path), self.local_scratch.path)
file_path = add_automation_tab(name="Test", lib_dir=self.local_scratch.path)
root = self.validate_file_exists_and_pyaedt_tabs_added(file_path)
panels = root.findall("./panel")
panel_names = [panel.attrib["label"] for panel in panels]
assert len(panel_names) == 1
assert len(panel_names) == 2

def test_04_write_to_existing_file_but_no_panels(self):
file_path = os.path.join(self.local_scratch.path, "TabConfig.xml")
file_path = os.path.join(self.local_scratch.path, "Project", "TabConfig.xml")
with open(file_path, "w") as fid:
fid.write(
"""<?xml version="1.0" encoding="UTF-8" standalone="no" ?>
Expand All @@ -88,7 +92,7 @@ def test_04_write_to_existing_file_but_no_panels(self):
</TabConfig>
"""
)
write_tab_config(os.path.dirname(file_path), self.local_scratch.path)
file_path = add_automation_tab(name="Test", lib_dir=self.local_scratch.path)
root = self.validate_file_exists_and_pyaedt_tabs_added(file_path)
junks = root.findall("./junk")
junk_names = [junk.attrib["label"] for junk in junks]
Expand All @@ -98,15 +102,13 @@ def test_04_write_to_existing_file_but_no_panels(self):
panel_names = [panel.attrib["label"] for panel in panels]
assert len(panel_names) == 1

def validate_file_exists_and_pyaedt_tabs_added(self, file_path):
@staticmethod
def validate_file_exists_and_pyaedt_tabs_added(file_path):
assert os.path.isfile(file_path) is True
assert ET.parse(file_path) is not None
tree = ET.parse(file_path)
root = tree.getroot()
panels = root.findall("./panel")
panel_names = [panel.attrib["label"] for panel in panels]
assert "Panel_PyAEDT" in panel_names
files_to_verify = ["images/large/pyansys.png", "images/gallery/PyAEDT.png"]
for file_name in files_to_verify:
assert os.path.isfile(os.path.join(os.path.dirname(file_path), file_name))
assert "Panel_PyAEDT_Toolkits" in panel_names
return root
38 changes: 15 additions & 23 deletions doc/source/Resources/PyAEDTInstallerFromDesktop.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,10 @@ def run_pyinstaller_from_c_python(oDesktop):
# enable in debu mode
# f.write("import sys\n")
# f.write('sys.path.insert(0, r"c:\\ansysdev\\git\\repos\\pyaedt")\n')
f.write("from pyaedt.misc.aedtlib_personalib_install import add_pyaedt_to_aedt\n")
f.write("from pyaedt.workflows.installer.pyaedt_installer import add_pyaedt_to_aedt\n")
f.write(
'add_pyaedt_to_aedt(aedt_version="{}", is_student_version={}, use_sys_lib=False, new_desktop_session=False, pers_dir=r"{}")\n'.format(
oDesktop.GetVersion()[:6], is_student_version(oDesktop), oDesktop.GetPersonalLibDirectory()))
'add_pyaedt_to_aedt(aedt_version="{}", student_version={}, new_desktop_session=False)\n'.format(
oDesktop.GetVersion()[:6], is_student_version(oDesktop)))

command = r'"{}" "{}"'.format(python_exe, python_script)
oDesktop.AddMessage("", "", 0, command)
Expand Down Expand Up @@ -119,6 +119,14 @@ def install_pyaedt():
if args.version < "232":
ld_library_path_dirs_to_add.append("{}/Delcross".format(args.edt_root))
os.environ["LD_LIBRARY_PATH"] = ":".join(ld_library_path_dirs_to_add) + ":" + os.getenv("LD_LIBRARY_PATH", "")
os.environ["TK_LIBRARY"] = ("{}/commonfiles/CPython/{}/linx64/Release/python/lib/tk8.5".
format(args.edt_root,
args.python_version.replace(
".", "_")))
os.environ["TCL_LIBRARY"] = ("{}/commonfiles/CPython/{}/linx64/Release/python/lib/tcl8.5".
format(args.edt_root,
args.python_version.replace(
".", "_")))

if not os.path.exists(venv_dir):

Expand All @@ -139,22 +147,20 @@ def install_pyaedt():
zip_ref.extractall(unzipped_path)

run_command(
'"{}" install --no-cache-dir --no-index --find-links={} pyaedt[all,dotnet]'.format(pip_exe, unzipped_path))
'"{}" install --no-cache-dir --no-index --find-links={} pyaedt[all,dotnet]'.format(pip_exe,
unzipped_path))
run_command(
'"{}" install --no-cache-dir --no-index --find-links={} jupyterlab'.format(pip_exe, unzipped_path))

else:
run_command('"{}" -m pip install --upgrade pip'.format(python_exe))
run_command('"{}" --default-timeout=1000 install wheel'.format(pip_exe))
run_command('"{}" --default-timeout=1000 install pyaedt[all]'.format(pip_exe))
# run_command('"{}" --default-timeout=1000 install git+https://github.com/ansys/pyaedt.git@main'.format(pip_exe))
Samuelopez-ansys marked this conversation as resolved.
Show resolved Hide resolved
# run_command(
# '"{}" --default-timeout=1000 install git+https://github.com/ansys/pyaedt.git@main'.format(pip_exe))
run_command('"{}" --default-timeout=1000 install jupyterlab'.format(pip_exe))
run_command('"{}" --default-timeout=1000 install ipython -U'.format(pip_exe))
run_command('"{}" --default-timeout=1000 install ipyvtklink'.format(pip_exe))
# User can uncomment these lines to install Pyside6 modules
# run_command('"{}" --default-timeout=1000 install pyside6==6.4.0'.format(pip_exe))
# run_command('"{}" --default-timeout=1000 install pyqtgraph'.format(pip_exe))
# run_command('"{}" --default-timeout=1000 install qdarkstyle'.format(pip_exe))

if args.version == "231":
run_command('"{}" uninstall -y pywin32'.format(pip_exe))
Expand All @@ -176,20 +182,6 @@ def install_pyaedt():
run_command('"{}" install --no-cache-dir --no-index --find-links={} pyaedt'.format(pip_exe, unzipped_path))
else:
run_command('"{}" --default-timeout=1000 install pyaedt[all]'.format(pip_exe))

# if is_windows:
# pyaedt_setup_script = "{}/Lib/site-packages/pyaedt/misc/aedtlib_personalib_install.py".format(venv_dir)
# else:
# pyaedt_setup_script = "{}/lib/python{}/site-packages/pyaedt/misc/aedtlib_personalib_install.py".format(
# venv_dir, args.python_version)
#
# if not os.path.isfile(pyaedt_setup_script):
# sys.exit("[ERROR] PyAEDT was not setup properly since {} file does not exist.".format(pyaedt_setup_script))
#
# command = '"{}" "{}" --version={}'.format(python_exe, pyaedt_setup_script, args.version)
# if args.student:
# command += " --student"
# run_command(command)
sys.exit(0)


Expand Down
169 changes: 2 additions & 167 deletions pyaedt/desktop.py
Original file line number Diff line number Diff line change
Expand Up @@ -1461,7 +1461,8 @@ def _exception(self, ex_value, tb_data):
tblist = tb_trace[0].split("\n")
self.logger.error(str(ex_value))
for el in tblist:
self.logger.error(el)
if el:
self.logger.error(el)

return str(ex_value)

Expand Down Expand Up @@ -1744,172 +1745,6 @@ def get_available_toolkits(self):

return list(available_toolkits.keys())

@pyaedt_function_handler()
def add_custom_toolkit(self, toolkit_name): # pragma: no cover
"""Add toolkit to AEDT Automation Tab.

Parameters
----------
toolkit_name : str
Name of toolkit to add.

Returns
-------
bool
"""
from pyaedt.misc.install_extra_toolkits import available_toolkits

toolkit = available_toolkits[toolkit_name]
toolkit_name = toolkit_name.replace("_", "")

def install(package_path, package_name=None):
executable = '"{}"'.format(sys.executable) if is_windows else sys.executable

commands = []
if package_path.startswith("git") and package_name:
commands.append([executable, "-m", "pip", "uninstall", "--yes", package_name])

commands.append([executable, "-m", "pip", "install", "--upgrade", package_path])

if self.aedt_version_id == "2023.1" and is_windows and "AnsysEM" in sys.base_prefix:
commands.append([executable, "-m", "pip", "uninstall", "--yes", "pywin32"])

for command in commands:
if is_linux:
p = subprocess.Popen(command)
else:
p = subprocess.Popen(" ".join(command))
p.wait()

install(toolkit["pip"], toolkit.get("package_name", None))
import site

packages = site.getsitepackages()
full_path = None
for pkg in packages:
if os.path.exists(os.path.join(pkg, toolkit["toolkit_script"])):
full_path = os.path.join(pkg, toolkit["toolkit_script"])
break
if not full_path:
raise FileNotFoundError("Error finding the package.")
self.add_script_to_menu(
toolkit_name=toolkit_name,
script_path=full_path,
script_image=toolkit,
product=toolkit["installation_path"],
copy_to_personal_lib=False,
add_pyaedt_desktop_init=False,
)

@pyaedt_function_handler()
def add_script_to_menu(
self,
toolkit_name,
script_path,
script_image=None,
product="Project",
copy_to_personal_lib=True,
add_pyaedt_desktop_init=True,
):
"""Add a script to the ribbon menu.

.. note::
This method is available in AEDT 2023 R2 and later. PyAEDT must be installed
in AEDT to allow this method to run. For more information, see `Installation
<https://aedt.docs.pyansys.com/version/stable/Getting_started/Installation.html>`_.

Parameters
----------
toolkit_name : str
Name of the toolkit to appear in AEDT.
script_path : str
Full path to the script file. The script will be moved to Personal Lib.
script_image : str, optional
Full path to the image logo (a 30x30 pixel PNG file) to add to the UI.
The default is ``None``.
product : str, optional
Product to which the toolkit applies. The default is ``"Project"``, in which case
it applies to all designs. You can also specify a product, such as ``"HFSS"``.
copy_to_personal_lib : bool, optional
Whether to copy the script to Personal Lib or link the original script. Default is ``True``.

Returns
-------
bool

"""
if not os.path.exists(script_path):
self.logger.error("Script does not exists.")
return False
from pyaedt.misc.install_extra_toolkits import write_toolkit_config

toolkit_dir = os.path.join(self.personallib, "Toolkits")
aedt_version = self.aedt_version_id
tool_dir = os.path.join(toolkit_dir, product, toolkit_name)
lib_dir = os.path.join(tool_dir, "Lib")
toolkit_rel_lib_dir = os.path.relpath(lib_dir, tool_dir)
if is_linux and aedt_version <= "2023.1":
toolkit_rel_lib_dir = os.path.join("Lib", toolkit_name)
lib_dir = os.path.join(toolkit_dir, toolkit_rel_lib_dir)
toolkit_rel_lib_dir = "../../" + toolkit_rel_lib_dir
os.makedirs(lib_dir, exist_ok=True)
os.makedirs(tool_dir, exist_ok=True)
dest_script_path = script_path
if copy_to_personal_lib:
dest_script_path = os.path.join(lib_dir, os.path.split(script_path)[-1])
shutil.copy2(script_path, dest_script_path)
files_to_copy = ["Run_PyAEDT_Toolkit_Script"]
executable_version_agnostic = sys.executable
for file_name in files_to_copy:
src = os.path.join(pathname, "misc", file_name + ".py_build")
dst = os.path.join(tool_dir, file_name.replace("_", " ") + ".py")
if not os.path.isfile(src):
raise FileNotFoundError("File not found: {}".format(src))
with open_file(src, "r") as build_file:
with open_file(dst, "w") as out_file:
self.logger.info("Building to " + dst)
build_file_data = build_file.read()
build_file_data = (
build_file_data.replace("##TOOLKIT_REL_LIB_DIR##", toolkit_rel_lib_dir)
.replace("##PYTHON_EXE##", executable_version_agnostic)
.replace("##PYTHON_SCRIPT##", dest_script_path)
)
build_file_data = build_file_data.replace(" % version", "")
out_file.write(build_file_data)
if aedt_version >= "2023.2":
if not script_image:
script_image = os.path.join(os.path.dirname(__file__), "misc", "images", "large", "pyansys.png")
write_toolkit_config(os.path.join(toolkit_dir, product), lib_dir, toolkit_name, toolkit=script_image)
self.logger.info("{} toolkit installed.".format(toolkit_name))
return True

@pyaedt_function_handler()
def remove_script_from_menu(self, toolkit_name, product="Project"):
"""Remove a toolkit script from the menu.

Parameters
----------
toolkit_name : str
Name of the toolkit to remove.
product : str, optional
Product to which the toolkit applies. The default is ``"Project"``, in which case
it applies to all designs. You can also specify a product, such as ``"HFSS"``.

Returns
-------
bool
"""
from pyaedt.misc.install_extra_toolkits import remove_toolkit_config

toolkit_dir = os.path.join(self.personallib, "Toolkits")
aedt_version = self.aedt_version_id
tool_dir = os.path.join(toolkit_dir, product, toolkit_name)
shutil.rmtree(tool_dir, ignore_errors=True)
if aedt_version >= "2023.2":
remove_toolkit_config(os.path.join(toolkit_dir, product), toolkit_name)
self.logger.info("{} toolkit removed successfully.".format(toolkit_name))
return True

@pyaedt_function_handler()
def submit_job(
self,
Expand Down
Loading
Loading