Skip to content

Commit

Permalink
chore: Use Identifable where possible
Browse files Browse the repository at this point in the history
The `Identifiable` type/utility was introduced later in the development
and it's not been applied everywhere yet, this fixes that
  • Loading branch information
tmpbeing committed Jan 10, 2024
1 parent 3f60e54 commit 7b65411
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 93 deletions.
67 changes: 41 additions & 26 deletions src/ansys/simai/core/data/geometries.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,16 @@
from ansys.simai.core.data.types import (
BoundaryConditions,
File,
Identifiable,
MonitorCallback,
NamedFile,
Range,
build_boundary_conditions,
get_id_from_identifiable,
get_object_from_identifiable,
unpack_named_file,
)
from ansys.simai.core.data.workspaces import Workspace
from ansys.simai.core.errors import InvalidArguments

if TYPE_CHECKING:
Expand Down Expand Up @@ -289,27 +293,29 @@ class GeometryDirectory(Directory[Geometry]):

def list(
self,
workspace_id: Optional[str] = None,
workspace: Optional[Identifiable[Workspace]] = None,
filters: Optional[Dict[str, Union[str, float, Range]]] = None,
) -> List[Geometry]:
"""List geometries from the server that belong to the currently set workspace or the specified one.
Args:
workspace_id: Id of the workspace to list geometries for. Necessary if no workspace is set for the client.
workspace: The id or :class:`model <.workspaces.Workspace>` of the workspace to list geometries for.
Necessary if no global workspace is set for the client.
filters: Optional filters. Only the elements with matching key-values in
their metadata will be returned. Each filter can be either:
- a string
- a numerical value (int or float)
- a :py:class:`Range`, to filter values matching a given numerical range of values
- a :class:`Range`, to filter values matching a given numerical range of values
Returns:
The list of all or filtered geometries on the server.
Raises:
TypeError: if a Range condition is applied on non-numerical metadata
"""
if not workspace_id:
workspace_id = self._client.current_workspace.id
workspace_id = get_id_from_identifiable(workspace, default=self._client._current_workspace)

# for now we filter ranges locally, so we filter
# local filters (ranges) from server filters (everything else)
ranges: Dict[str, Range] = {}
Expand Down Expand Up @@ -352,22 +358,22 @@ def get(
self,
name: Optional[str] = None,
id: Optional[str] = None,
workspace_id: Optional[str] = None,
workspace: Optional[Identifiable[Workspace]] = None,
) -> Geometry:
"""Get a specific geometry object from the server, by name or by id.
Args:
name: The name of the geometry to get, optional.
id: The id of the geometry to get, optional
workspace_id: The id of the workspace containing the geometry. Only necessary if using
name and no workspace is set for the client
workspace: The id or :class:`model <.workspaces.Workspace>` of the workspace containing the geometry.
Necessary when using name and no global workspace is set for the client
Returns:
A :py:class:`Geometry`
Raises:
ValueError: If neither name nor id is given
InvalidArguments: If neither name nor id is given
NotFoundError: No geometry with the given name or id exists
Examples:
Expand All @@ -388,37 +394,42 @@ def get(
geometry = simai.geometries.get(id="abcdef12")
"""
if name and id:
raise ValueError("Name and Id cannot be both specified.")
raise InvalidArguments("Name and Id cannot be both specified.")
if name:
if not workspace_id:
workspace_id = self._client.current_workspace.id
return self._model_from(self._client._api.get_geometry_by_name(name, workspace_id))
return self._model_from(
self._client._api.get_geometry_by_name(
name,
get_id_from_identifiable(workspace, default=self._client._current_workspace),
)
)
if id:
return self._model_from(self._client._api.get_geometry(id))
raise ValueError("Either the name or the id must be specified.")
raise InvalidArguments("Either the name or the id must be specified.")

def delete(self, geometry_id: str) -> None:
def delete(self, geometry: Identifiable[Geometry]) -> None:
"""Delete a specific geometry and its data from the server.
All the objects associated to this geometry (predictions and post-processings)
are also deleted.
Args:
geometry_id: The id of the geometry to delete
geometry: The id or :class:`model <Geometry>` of the geometry to delete
Raises:
NotFoundError: No geometry with the given id exists
See Also:
:func:`Geometry.delete`
"""
self._client._api.delete_geometry(geometry_id)
self._client._api.delete_geometry(get_id_from_identifiable(geometry))

def upload( # noqa: D417
self,
file: NamedFile,
metadata: Optional[Dict[str, Any]] = None,
workspace_id: Optional[str] = None,
workspace: Optional[Identifiable[Workspace]] = None,
monitor_callback: Optional[MonitorCallback] = None,
**kwargs,
) -> Geometry:
"""Upload a geometry to SimAI's platform.
Expand All @@ -427,17 +438,16 @@ def upload( # noqa: D417
See :class:`~ansys.simai.core.data.types.NamedFile` for more details.
metadata: Optional metadatas to add to the geometry, simple key-value store.
Lists and nested objects are not supported.
workspace_id: Id of the workspace in which the geometry will be uploaded,
only necessary if no workspace is set for the client.
workspace: The id or :class:`model <.workspaces.Workspace>` in which the geometry will be uploaded.
Necessary if no workspace is set for the client.
monitor_callback: An optional callback to monitor the progress of the download.
See :obj:`~ansys.simai.core.data.types.MonitorCallback` for details.
Returns:
The created :py:class:`Geometry` object
"""
if not workspace_id:
workspace_id = self._client.current_workspace.id
workspace_id = get_id_from_identifiable(workspace, default=self._client._current_workspace)
workspace = self._client.workspaces.get(workspace_id)
with unpack_named_file(file) as (readable_file, name, extension):
if extension not in workspace.model.geometry["accepted_file_formats"]:
Expand All @@ -459,14 +469,14 @@ def upload( # noqa: D417

def download(
self,
geometry_id: str,
geometry: Identifiable[Geometry],
file: Optional[File] = None,
monitor_callback: Optional[MonitorCallback] = None,
) -> Union[None, BinaryIO]:
"""Downloads the geometry with the given id into the file at the given path.
Args:
geometry_id: The id of the geometry to download
geometry: The id or :class:`model <Geometry>` of the geometry to download
file: An optional binary file-object or the path of the file to put the
content into
monitor_callback: An optional callback to monitor the progress of the download.
Expand All @@ -478,11 +488,13 @@ def download(
See Also:
:func:`Geometry.download`
"""
return self._client._api.download_geometry(geometry_id, file, monitor_callback)
return self._client._api.download_geometry(
get_id_from_identifiable(geometry), file, monitor_callback
)

def sweep(
self,
candidate_geometry: "Geometry",
candidate_geometry: Identifiable[Geometry],
swept_metadata: Optional[Union[str, List[str]]] = None,
fixed_metadata: Optional[List[str]] = None,
geometries: Optional[List["Geometry"]] = None,
Expand All @@ -507,6 +519,9 @@ def sweep(
See Also:
:func:`Geometry.sweep`
"""
candidate_geometry = get_object_from_identifiable(
candidate_geometry, self._client.geometries
)
return candidate_geometry.sweep(
swept_metadata=swept_metadata,
fixed_metadata=fixed_metadata,
Expand Down
27 changes: 14 additions & 13 deletions src/ansys/simai/core/data/post_processings.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@

from ansys.simai.core.data.base import ComputableDataModel, Directory
from ansys.simai.core.data.downloads import DownloadableResult
from ansys.simai.core.data.types import Identifiable, get_id_from_identifiable
from ansys.simai.core.errors import InvalidArguments, InvalidServerStateError
from ansys.simai.core.utils.numerical import convert_axis_and_coordinate_to_plane_eq_coeffs

Expand Down Expand Up @@ -623,7 +624,7 @@ def get(self, id: str) -> PostProcessing:
def list(
self,
post_processing_type: Optional[Type[PostProcessing]] = None,
prediction_id: Optional[str] = None,
prediction: Optional[Identifiable["Prediction"]] = None,
) -> List[PostProcessing]:
"""List the post-processings in the current workspace or associated to a prediction.
Expand All @@ -633,7 +634,8 @@ def list(
Args:
post_processing_type: The type of post-processing to list
prediction_id: The id of a prediction, if given will only return post-processings associated to it
prediction: The id or :class:`model <.predictions.Prediction>` of a prediction,
if given the method will only return post-processings associated to it
Raises:
NotFoundError: The post-processing type and/or the prediction id are incorrect.
Expand All @@ -650,6 +652,7 @@ def list(
)
"""
pp_type_str = post_processing_type._api_name() if post_processing_type else None
prediction_id = get_id_from_identifiable(prediction, required=False)
if not prediction_id:
post_processings = self._client._api.get_post_processings_in_workspace(
self._client.current_workspace.id, pp_type_str
Expand All @@ -663,7 +666,7 @@ def list(
def run(
self,
post_processing_type: Union[str, Type[PostProcessing]],
prediction_id: str,
prediction: Identifiable["Prediction"],
parameters: Optional[Dict[str, Any]] = None,
**kwargs,
) -> PostProcessing:
Expand All @@ -674,8 +677,8 @@ def run(
Note that the case of the class names must be respected.
Args:
prediction_id: The id of the prediction for which to run the post-processing.
post_processing_type: The type of post-processing to run, as a string or as the class itself
prediction: The id or :class:`model <.predictions.Prediction>` of the prediction for which to run the post-processing.
parameters: The parameters to apply to the post-processing, if needed. Alternatively, parameters can be passed as kwargs.
**kwargs: Unpacked parameters for the post-processing
Expand All @@ -687,16 +690,14 @@ def run(
simai = ansys.simai.core_from_config()
prediction = simai.predictions.list()[0]
simai.post_processings.run(
ansys.simai.core.Slice, prediction.id, {"axis": "x", coordinate: 50}
ansys.simai.core.Slice, prediction, {"axis": "x", coordinate: 50}
)
Using kwargs:
.. code-block:: python
simai.post_processings.run(
ansys.simai.core.Slice, prediction.id, axis="x", coordinate=50
)
simai.post_processings.run(ansys.simai.core.Slice, prediction, axis="x", coordinate=50)
"""
if isinstance(post_processing_type, str):
post_processing_type = getattr(
Expand All @@ -715,18 +716,18 @@ def run(
"""
)
)
prediction = self._client.predictions.get(prediction_id)
prediction = self._client.predictions.get(get_id_from_identifiable(prediction))
if not parameters:
parameters = {}
parameters.update(**kwargs)
return prediction.post._get_or_run(pp_class, parameters, True)

def delete(self, id):
"""Delete a post-processing by id.
def delete(self, post_processing: Identifiable[PostProcessing]):
"""Delete a post-processing.
Args:
id: The id of the post-processing to delete
post_processing: The id or :class:`model <PostProcessing>` of the post-processing to delete
"""
# FIXME?: This won't update the post_processings of the prediction's PredictionPostProcessings if any.
# Doing so would require an extra call to get the prediction info and I'm not sure there's really a point
self._client._api.delete_post_processing(id)
self._client._api.delete_post_processing(get_id_from_identifiable(post_processing))
Loading

0 comments on commit 7b65411

Please sign in to comment.