Skip to content

FEAT: 6290 Add Profile class #6478

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

Closed
wants to merge 36 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
a9da59c
Update create_setup method
Devin-Crawford Jun 18, 2025
6ad4cc5
chore: adding changelog file 6279.added.md [dependabot-skip]
pyansys-ci-bot Jun 18, 2025
7f5928b
CHORE: Auto fixes from pre-commit hooks
pre-commit-ci[bot] Jun 18, 2025
1429c54
Add new classes
Devin-Crawford Jun 24, 2025
a13e483
Merge branch 'main' into feat/6290_profile_class
Devin-Crawford Jul 12, 2025
036f18b
Update archive project name
Devin-Crawford Jul 14, 2025
33c9381
Add profile.py
Devin-Crawford Jul 15, 2025
9dc279e
Update profile.py
Devin-Crawford Jul 15, 2025
82466f2
Update design.py
Devin-Crawford Jul 17, 2025
a85428e
Merge branch 'fix/6400-use-archive-name-for-project' into feat/6290_p…
Devin-Crawford Jul 17, 2025
62f9576
CHORE: Auto fixes from pre-commit hooks
pre-commit-ci[bot] Jul 17, 2025
9b0ef11
Update design.py
Devin-Crawford Jul 17, 2025
2f3acbc
Merge remote-tracking branch 'origin/fix/6400-use-archive-name-for-pr…
Devin-Crawford Jul 17, 2025
7b982a8
Merge branch 'fix/6400-use-archive-name-for-project' into feat/6290_p…
Devin-Crawford Jul 17, 2025
70d070e
Update design.py
Devin-Crawford Jul 17, 2025
21009eb
Update design.py
Devin-Crawford Jul 18, 2025
a9836d6
Merge branch 'main' into fix/6400-use-archive-name-for-project
Devin-Crawford Jul 22, 2025
937fa6e
Add Profile class
Devin-Crawford Jul 28, 2025
b521539
Add Unit Tests
Devin-Crawford Jul 30, 2025
0712c9b
CHORE: Auto fixes from pre-commit hooks
pre-commit-ci[bot] Jul 30, 2025
9886f59
chore: adding changelog file 6478.added.md [dependabot-skip]
pyansys-ci-bot Jul 30, 2025
01576e4
Fixed docstring
maxcapodi78 Jul 30, 2025
de25f9e
Merge branch 'main' into feat/6290_profile_class
Devin-Crawford Jul 31, 2025
e8fb73a
Update profile class
Devin-Crawford Aug 9, 2025
f208a9b
Merge remote-tracking branch 'origin/main' into feat/6290_profile_class
Devin-Crawford Aug 9, 2025
068bc26
Merge branch 'main' into fix/6400-use-archive-name-for-project
Devin-Crawford Aug 11, 2025
ae6ac39
Update unit test
Devin-Crawford Aug 12, 2025
b5ec8e6
CHORE: Auto fixes from pre-commit hooks
pre-commit-ci[bot] Aug 12, 2025
c0b493f
Correct error
Devin-Crawford Aug 12, 2025
49320de
Correct error
Devin-Crawford Aug 12, 2025
6cb3d89
Update test_01_Design.py
Devin-Crawford Aug 13, 2025
591d560
CHORE: Auto fixes from pre-commit hooks
pre-commit-ci[bot] Aug 13, 2025
7fffb5a
Merge branch 'main' into fix/6400-use-archive-name-for-project
Devin-Crawford Aug 13, 2025
7378f08
Merge branch 'fix/6400-use-archive-name-for-project' into feat/6290_p…
Devin-Crawford Aug 13, 2025
e267c0a
CHORE: Auto fixes from pre-commit hooks
pre-commit-ci[bot] Aug 13, 2025
4fd1aad
chore: adding changelog file 6478.added.md [dependabot-skip]
pyansys-ci-bot Aug 13, 2025
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
1 change: 1 addition & 0 deletions doc/changelog.d/6279.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Update create_setup method
1 change: 1 addition & 0 deletions doc/changelog.d/6478.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
6290 Add Profile class
102 changes: 56 additions & 46 deletions src/ansys/aedt/core/application/design.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
from ansys.aedt.core.generic.constants import AEDT_UNITS
from ansys.aedt.core.generic.constants import unit_system
from ansys.aedt.core.generic.data_handlers import variation_string_to_dict
from ansys.aedt.core.generic.file_utils import available_file_name
from ansys.aedt.core.generic.file_utils import check_and_download_file
from ansys.aedt.core.generic.file_utils import generate_unique_name
from ansys.aedt.core.generic.file_utils import is_project_locked
Expand Down Expand Up @@ -102,8 +103,8 @@ def load_aedt_thread(project_path) -> None:
Path to the AEDT project file.
"""
pp = load_entire_aedt_file(project_path)
inner_project_settings.properties[Path(project_path)] = pp
inner_project_settings.time_stamp = Path(project_path).stat().st_mtime
inner_project_settings.properties[os.path.normpath(project_path)] = pp
inner_project_settings.time_stamp = os.path.getmtime(project_path)


class Design(AedtObjects):
Expand Down Expand Up @@ -176,13 +177,11 @@ def __init__(
self._project_name: Optional[str] = None
self._project_path: Optional[str] = None
self.__t: Optional[threading.Thread] = None
if isinstance(project_name, Path):
project_name = str(project_name)
if (
is_windows
and project_name
and Path(project_name).exists()
and (Path(project_name).suffix == ".aedt" or Path(project_name).suffix == ".a3dcomp")
and os.path.exists(project_name)
and (os.path.splitext(project_name)[1] == ".aedt" or os.path.splitext(project_name)[1] == ".a3dcomp")
):
self.__t = threading.Thread(target=load_aedt_thread, args=(project_name,), daemon=True)
self.__t.start()
Expand Down Expand Up @@ -242,7 +241,7 @@ def __init__(
self._logger.odesign = self.odesign
AedtObjects.__init__(self, self._desktop_class, self.oproject, self.odesign, is_inherithed=True)
self.logger.info("Aedt Objects correctly read")
if is_windows and not self.__t and not settings.lazy_load and Path(self.project_file).exists():
if is_windows and not self.__t and not settings.lazy_load and os.path.exists(self.project_file):
self.__t = threading.Thread(target=load_aedt_thread, args=(self.project_file,), daemon=True)
self.__t.start()

Expand Down Expand Up @@ -626,27 +625,29 @@ def project_properties(self) -> Dict[str, dict]:
self.__t = None
start = time.time()
if self.project_timestamp_changed or (
Path(self.project_file).exists()
and Path(self.project_file).resolve() not in inner_project_settings.properties
os.path.exists(self.project_file)
and os.path.normpath(self.project_file) not in inner_project_settings.properties
):
inner_project_settings.properties[Path(self.project_file).resolve()] = load_entire_aedt_file(
inner_project_settings.properties[os.path.normpath(self.project_file)] = load_entire_aedt_file(
self.project_file
)
self._logger.info(f"aedt file load time {time.time() - start}")
elif (
Path(self.project_file).resolve() not in inner_project_settings.properties
os.path.normpath(self.project_file) not in inner_project_settings.properties
and settings.remote_rpc_session
and settings.remote_rpc_session.filemanager.pathexists(str(Path(self.project_file).resolve()))
and settings.remote_rpc_session.filemanager.pathexists(self.project_file)
):
file_path = check_and_download_file(str(Path(self.project_file).resolve()))
file_path = check_and_download_file(self.project_file)
try:
inner_project_settings.properties[Path(self.project_file).resolve()] = load_entire_aedt_file(file_path)
inner_project_settings.properties[os.path.normpath(self.project_file)] = load_entire_aedt_file(
file_path
)
except Exception:
self._logger.info("Failed to load AEDT file.")
else:
self._logger.info(f"Time to load AEDT file: {time.time() - start}.")
if Path(self.project_file).resolve() in inner_project_settings.properties:
return inner_project_settings.properties[Path(self.project_file).resolve()]
if os.path.normpath(self.project_file) in inner_project_settings.properties:
return inner_project_settings.properties[os.path.normpath(self.project_file)]
return {}

@property
Expand Down Expand Up @@ -845,8 +846,8 @@ def project_path(self) -> Optional[str]:
@property
def project_time_stamp(self) -> Union[int, float]:
"""Return Project time stamp."""
if Path(self.project_file).exists():
inner_project_settings.time_stamp = Path(self.project_file).stat().st_mtime
if os.path.exists(self.project_file):
inner_project_settings.time_stamp = os.path.getmtime(self.project_file)
else:
inner_project_settings.time_stamp = 0
return inner_project_settings.time_stamp
Expand All @@ -868,7 +869,7 @@ def project_file(self) -> Optional[str]:

"""
if self.project_path:
return str(Path(self.project_path) / (self.project_name + ".aedt"))
return os.path.join(self.project_path, self.project_name + ".aedt")

@property
def lock_file(self) -> Optional[str]:
Expand All @@ -881,7 +882,7 @@ def lock_file(self) -> Optional[str]:

"""
if self.project_path:
return str(Path(self.project_path) / (self.project_name + ".aedt.lock"))
return os.path.join(self.project_path, self.project_name + ".aedt.lock")

@property
def results_directory(self) -> Optional[str]:
Expand All @@ -894,7 +895,7 @@ def results_directory(self) -> Optional[str]:

"""
if self.project_path:
return str(Path(self.project_path) / (self.project_name + ".aedtresults"))
return os.path.join(self.project_path, self.project_name + ".aedtresults")

@property
def solution_type(self) -> Optional[str]:
Expand Down Expand Up @@ -996,7 +997,7 @@ def src_dir(self) -> str:
Full absolute path for the ``python`` directory.

"""
return str(Path(__file__).parent.resolve())
return os.path.dirname(os.path.realpath(__file__))

@property
def pyaedt_dir(self) -> str:
Expand All @@ -1008,7 +1009,7 @@ def pyaedt_dir(self) -> str:
Full absolute path for the ``pyaedt`` directory.

"""
return str(Path(self.src_dir).parent.resolve())
return os.path.realpath(os.path.join(self.src_dir, ".."))

@property
def library_list(self) -> List[str]:
Expand Down Expand Up @@ -1048,7 +1049,7 @@ def toolkit_directory(self) -> str:
name = self.project_name.replace(" ", "_")
else:
name = generate_unique_name("prj")
toolkit_directory = Path(self.project_path) / (name + ".pyaedt")
toolkit_directory = os.path.join(self.project_path, name + ".pyaedt")
if settings.remote_rpc_session:
toolkit_directory = self.project_path + "/" + name + ".pyaedt"
try:
Expand All @@ -1057,7 +1058,7 @@ def toolkit_directory(self) -> str:
toolkit_directory = settings.remote_rpc_session.filemanager.temp_dir() + "/" + name + ".pyaedt"
elif settings.remote_api or settings.remote_rpc_session:
toolkit_directory = self.results_directory
elif not Path(toolkit_directory).is_dir():
elif not os.path.isdir(toolkit_directory):
try:
os.makedirs(toolkit_directory)
except FileNotFoundError:
Expand All @@ -1080,16 +1081,16 @@ def working_directory(self) -> str:
name = self.design_name.replace(" ", "_")
else:
name = generate_unique_name("prj")
working_directory = (Path(self.toolkit_directory) / name).resolve()
working_directory = os.path.join(os.path.normpath(self.toolkit_directory), name)
if settings.remote_rpc_session:
working_directory = self.toolkit_directory + "/" + name
settings.remote_rpc_session.filemanager.makedirs(working_directory)
elif not Path(working_directory).is_dir():
elif not os.path.isdir(working_directory):
try:
Path(working_directory).mkdir()
os.makedirs(working_directory)
except FileNotFoundError:
working_directory = Path(self.toolkit_directory) / (name + ".results")
return str(working_directory)
working_directory = os.path.join(self.toolkit_directory, name + ".results")
return working_directory

@property
def default_solution_type(self) -> str:
Expand Down Expand Up @@ -1254,13 +1255,13 @@ def oproject(self, proj_name=None):
elif Path(proj_name).exists() or (
settings.remote_rpc_session and settings.remote_rpc_session.filemanager.pathexists(proj_name)
):
if ".aedtz" in proj_name:
name = self._generate_unique_project_name()
path = Path(proj_name).parent
self.odesktop.RestoreProjectArchive(proj_name, str(Path(path) / name), True, True)
if Path(proj_name).suffix == ".aedtz":
name = available_file_name(Path(proj_name).with_suffix(".aedt"))
self.odesktop.RestoreProjectArchive(str(proj_name), str(name), True, True)
time.sleep(0.5)
proj_name = name[:-5]
proj_name = save_to_file.stem
self._oproject = self.desktop_class.active_project(proj_name)
self._oproject = self.desktop_class.active_project(name.stem)
self._add_handler()
self.logger.info(f"Archive {proj_name} has been restored to project {self._oproject.GetName()}")
elif ".def" in proj_name or proj_name[-5:] == ".aedb":
Expand Down Expand Up @@ -2699,7 +2700,7 @@ def load_project(self, file_name, design=None, close_active=False, set_active=Fa
----------
>>> oDesktop.OpenProject
"""
proj = self.odesktop.OpenProject(file_name)
proj = self.odesktop.OpenProject(str(file_name))
if close_active and self.oproject:
self._close_edb()
self.close_project(self.project_name, save=set_active)
Expand Down Expand Up @@ -3206,7 +3207,7 @@ def change_validation_settings(
return True

@pyaedt_function_handler()
def clean_proj_folder(self, directory=None, name=None):
def clean_proj_folder(self, directory: Optional[Union[str, Path]] = None, name=None):
"""Delete a project folder.

Parameters
Expand Down Expand Up @@ -3578,6 +3579,9 @@ def _generate_unique_project_name(self):
def rename_design(self, name, save=True):
"""Rename the active design.

.. depreccated:: 0.19.0
Use :py:meth:`design_name` property setter instead.

Parameters
----------
name : str
Expand All @@ -3595,6 +3599,11 @@ def rename_design(self, name, save=True):
----------
>>> oDesign.RenameDesignInstance
"""
warnings.warn(
"`rename_design()` is deprecated. Assign the new design name to " + "`app.design_name` instead.",
DeprecationWarning,
)

self._odesign.RenameDesignInstance(self.design_name, name)
self._design_name = None
if save:
Expand All @@ -3603,12 +3612,12 @@ def rename_design(self, name, save=True):
return True

@pyaedt_function_handler(project_fullname="project", design_name="design")
def copy_design_from(self, project, design, save_project=True, set_active_design=True):
def copy_design_from(self, project: Union[str, Path], design, save_project=True, set_active_design=True):
"""Copy a design from a project into the active project.

Parameters
----------
project : str
project : str, :class:`pathlib.Path`
Full path and name for the project containing the design to copy.
The active design is maintained.
design : str
Expand All @@ -3633,9 +3642,10 @@ def copy_design_from(self, project, design, save_project=True, set_active_design
>>> oProject.Paste
"""
self.save_project()
project = Path(project)
# open the origin project
if Path(project).exists():
proj_from = self.odesktop.OpenProject(project)
if project.exists():
proj_from = self.odesktop.OpenProject(str(project))
proj_from_name = proj_from.GetName()
else:
return None
Expand Down Expand Up @@ -3797,12 +3807,12 @@ def read_design_data(self):
return design_data

@pyaedt_function_handler(project_file="file_name", refresh_obj_ids_after_save="refresh_ids")
def save_project(self, file_name=None, overwrite=True, refresh_ids=False):
def save_project(self, file_name: Optional[Union[Path, str]] = None, overwrite=True, refresh_ids=False):
"""Save the project and add a message.

Parameters
----------
file_name : str, optional
file_name : str or :class:`pathlib.Path`, optional
Full path and project name. The default is ````None``.
overwrite : bool, optional
Whether to overwrite the existing project. The default is ``True``.
Expand Down Expand Up @@ -3845,7 +3855,7 @@ def save_project(self, file_name=None, overwrite=True, refresh_ids=False):
@pyaedt_function_handler(project_file="project_path", additional_file_lists="additional_files")
def archive_project(
self,
project_path=None,
project_path: Union[str, Path] = None,
include_external_files=True,
include_results_file=True,
additional_files=None,
Expand All @@ -3855,7 +3865,7 @@ def archive_project(

Parameters
----------
project_path : str, optional
project_path : str, :class:`pathlib.Path` optional
Full path and project name. The default is ``None``.
include_external_files : bool, optional
Whether to include external files in the archive. The default is ``True``.
Expand All @@ -3881,7 +3891,7 @@ def archive_project(
msg_text = f"Saving {self.project_name} Project"
self.logger.info(msg_text)
if not project_path:
project_path = Path(self.project_path) / (self.project_name + ".aedtz")
project_path = available_file_name(Path(self.project_path) / (self.project_name + ".aedtz"))
self.oproject.Save()
self.oproject.SaveProjectArchive(
str(project_path), include_external_files, include_results_file, additional_files, notes
Expand Down
14 changes: 7 additions & 7 deletions src/ansys/aedt/core/desktop.py
Original file line number Diff line number Diff line change
Expand Up @@ -794,7 +794,7 @@ def src_dir(self):

Returns
-------
str
class:`pathlib.Path`
Full absolute path for the ``python`` directory.

"""
Expand All @@ -806,19 +806,19 @@ def syslib(self):

Returns
-------
str
Full absolute path for the ``SysLib`` directory.
:class:`pathlib.Path`
Path to the ``SysLib``.

"""
return self.odesktop.GetLibraryDirectory()
return Path(self.odesktop.GetLibraryDirectory())

@property
def pyaedt_dir(self):
"""PyAEDT directory.

Returns
-------
str
:class:`pathlib.Path`
Full absolute path for the ``pyaedt`` directory.

"""
Expand Down Expand Up @@ -1072,7 +1072,7 @@ def project_path(self, project_name=None):

Returns
-------
str
:class:`pathlib.Path`
Path to the project.

"""
Expand All @@ -1081,7 +1081,7 @@ def project_path(self, project_name=None):
else:
oproject = self.active_project(project_name)
if oproject:
return oproject.GetPath()
return Path(oproject.GetPath())
return None

@pyaedt_function_handler()
Expand Down
27 changes: 27 additions & 0 deletions src/ansys/aedt/core/generic/file_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,33 @@ def generate_unique_project_name(
return str(prj)


@pyaedt_function_handler()
def available_file_name(full_file_name: Union[str, Path]) -> Path:
"""Provide a file name that doesn't exist.

If the input file name exists, increment the base
name and return a valid ``Path`` object with an updated name.

Parameters
----------
full_file_name : str or :class:`pathlib.Path`
File to be saved.

Returns
-------
class:`pathlib.Path`
Valid file name with increment suffix `"_n.ext"`. If the file doesn't
exist, the original file name will be returned as a ``Path`` object.
"""
p = Path(full_file_name)
candidate = p
n = 1
while candidate.exists():
candidate = candidate.with_name(f"{p.stem}_{n}{p.suffix}")
n += 1
return candidate


@pyaedt_function_handler(startpath="path", filepattern="file_pattern")
def recursive_glob(path: Union[str, Path], file_pattern: str):
"""Get a list of files matching a pattern, searching recursively from a start path.
Expand Down
Loading
Loading