Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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/648.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Feat: Added APIs delete_event() and delete_phase()
12 changes: 12 additions & 0 deletions src/ansys/sherlock/core/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -1304,3 +1304,15 @@ def __init__(self, message):
def __str__(self):
"""Format error message."""
return f"Save profile error: {self.message}"


class SherlockDeleteError(Exception):
"""Contains the errors raised when an event or a phase cannot be deleted."""

def __init__(self, message):
"""Initialize error message."""
self.message = message

def __str__(self):
"""Format error message."""
return f"Delete error: {self.message}"
86 changes: 86 additions & 0 deletions src/ansys/sherlock/core/lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
SherlockAddThermalEventError,
SherlockAddThermalProfilesError,
SherlockCreateLifePhaseError,
SherlockDeleteError,
SherlockInvalidHarmonicProfileEntriesError,
SherlockInvalidLoadDirectionError,
SherlockInvalidOrientationError,
Expand All @@ -39,6 +40,8 @@
)
from ansys.sherlock.core.grpc_stub import GrpcStub
from ansys.sherlock.core.types.lifecycle_types import (
DeleteEventRequest,
DeletePhaseRequest,
ImportThermalSignalRequest,
SaveHarmonicProfileRequest,
SaveRandomVibeProfileRequest,
Expand Down Expand Up @@ -2353,3 +2356,86 @@ def save_thermal_profile(
# Raise error if save failed
if response.value != 0:
raise SherlockSaveProfileError(response.message)

@require_version(261)
def delete_event(self, request: DeleteEventRequest) -> SherlockCommonService_pb2.ReturnCode:
"""Delete a life cycle event from a given phase in a project.

Available Since: 2026R1

Parameters
----------
request : DeleteEventRequest
Request object containing project, phase name, and event name.

Returns
-------
SherlockCommonService_pb2.ReturnCode
Status code of the response. 0 for success.

Examples
--------
>>> from ansys.sherlock.core.types.lifecycle_types import DeleteEventRequest
>>> from ansys.sherlock.core.launcher import launch_sherlock
>>> sherlock = launch_sherlock()
>>> response = sherlock.lifecycle.delete_event(
>>> DeleteEventRequest(
>>> project="MyProject",
>>> phase_name="ThermalPhase",
>>> event_name="ThermalCycle_A",
>>> )
>>> )
>>> assert response.value == 0
"""
grpc_request = request._convert_to_grpc()

if not self._is_connection_up():
raise SherlockNoGrpcConnectionException()

response = self.stub.deleteEvent(grpc_request)

if response.value != 0:
raise SherlockDeleteError(response.message)

return response

@require_version(261)
def delete_phase(self, request: DeletePhaseRequest) -> SherlockCommonService_pb2.ReturnCode:
"""Delete a life cycle phase from a project.

Available Since: 2026R1

Parameters
----------
request : DeletePhaseRequest
Request object containing project and phase name.

Returns
-------
SherlockCommonService_pb2.ReturnCode
Status code of the response. 0 for success.

Examples
--------
>>> from ansys.sherlock.core.types.lifecycle_types import DeletePhaseRequest
>>> from ansys.sherlock.core.launcher import launch_sherlock
>>> sherlock = launch_sherlock()
>>> response = sherlock.lifecycle.delete_phase(
>>> DeletePhaseRequest(
>>> project="MyProject",
>>> phase_name="ThermalPhase",
>>> )
>>> )
>>> assert response.value == 0
"""
grpc_request = request._convert_to_grpc()

if not self._is_connection_up():
raise SherlockNoGrpcConnectionException()

response = self.stub.deletePhase(grpc_request)

if response.value != 0:
raise SherlockDeleteError(response.message)

return response
50 changes: 50 additions & 0 deletions src/ansys/sherlock/core/types/lifecycle_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -252,3 +252,53 @@ def _convert_to_grpc(self) -> SherlockLifeCycleService_pb2.SaveThermalProfileReq
eventName=self.event_name,
filePath=self.file_path,
)


class DeleteEventRequest(BaseModel):
"""Request to delete a life cycle event from a project phase."""

project: str
"""Sherlock project name."""

phase_name: str
"""The name of the life cycle phase from which to delete this event."""

event_name: str
"""Name of the event to be deleted."""

@field_validator("project", "phase_name", "event_name")
@classmethod
def str_validation(cls, value: str, info: ValidationInfo):
"""Validate string fields listed."""
return basic_str_validator(value, info.field_name)

def _convert_to_grpc(self) -> SherlockLifeCycleService_pb2.DeleteEventRequest:
"""Convert to gRPC DeleteEventRequest."""
return SherlockLifeCycleService_pb2.DeleteEventRequest(
project=self.project,
phaseName=self.phase_name,
eventName=self.event_name,
)


class DeletePhaseRequest(BaseModel):
"""Request to delete a life cycle phase from a project."""

project: str
"""Sherlock project name."""

phase_name: str
"""Name of the life cycle phase to be deleted."""

@field_validator("project", "phase_name")
@classmethod
def str_validation(cls, value: str, info: ValidationInfo):
"""Validate string fields listed."""
return basic_str_validator(value, info.field_name)

def _convert_to_grpc(self) -> SherlockLifeCycleService_pb2.DeletePhaseRequest:
"""Convert to gRPC DeletePhaseRequest."""
return SherlockLifeCycleService_pb2.DeletePhaseRequest(
project=self.project,
phaseName=self.phase_name,
)
134 changes: 134 additions & 0 deletions tests/test_lifecycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
SherlockAddThermalEventError,
SherlockAddThermalProfilesError,
SherlockCreateLifePhaseError,
SherlockDeleteError,
SherlockLoadHarmonicProfileError,
SherlockLoadRandomVibeProfileError,
SherlockLoadShockProfileDatasetError,
Expand All @@ -25,6 +26,8 @@
)
from ansys.sherlock.core.lifecycle import Lifecycle
from ansys.sherlock.core.types.lifecycle_types import (
DeleteEventRequest,
DeletePhaseRequest,
ImportThermalSignalRequest,
SaveHarmonicProfileRequest,
SaveRandomVibeProfileRequest,
Expand Down Expand Up @@ -62,6 +65,9 @@ def test_all():
helper_test_save_shock_pulse_profile(lifecycle)
helper_test_save_thermal_profile(lifecycle)

helper_test_delete_event(lifecycle, shock_event_name, phase_name)
helper_test_delete_phase(lifecycle, phase_name)


def helper_test_create_life_phase(lifecycle: Lifecycle):
"""Test create_life_phase API"""
Expand Down Expand Up @@ -2618,5 +2624,133 @@ def helper_test_save_thermal_profile(lifecycle: Lifecycle):
assert type(e) == SherlockSaveProfileError


def helper_test_delete_event(lifecycle: Lifecycle, event_name: str, phase_name: str):
# project missing
try:
lifecycle.delete_event(
DeleteEventRequest(
project="",
phase_name="On The Road",
event_name="ThermalCycle_A",
)
)
pytest.fail("No exception raised when using a missing project parameter")
except Exception as e:
assert isinstance(e, pydantic.ValidationError)
assert str(e.errors()[0]["msg"]) == (
"Value error, project is invalid because it is None or empty."
)

# phase_name missing
try:
lifecycle.delete_event(
DeleteEventRequest(
project="Tutorial Project",
phase_name="",
event_name="ThermalCycle_A",
)
)
pytest.fail("No exception raised when using a missing phase_name parameter")
except Exception as e:
assert isinstance(e, pydantic.ValidationError)
assert str(e.errors()[0]["msg"]) == (
"Value error, phase_name is invalid because it is None or empty."
)

# event_name missing
try:
lifecycle.delete_event(
DeleteEventRequest(
project="Tutorial Project",
phase_name="On The Road",
event_name="",
)
)
pytest.fail("No exception raised when using a missing event_name parameter")
except Exception as e:
assert isinstance(e, pydantic.ValidationError)
assert str(e.errors()[0]["msg"]) == (
"Value error, event_name is invalid because it is None or empty."
)

if lifecycle._is_connection_up():
# valid request but false event_name
try:
lifecycle.delete_event(
DeleteEventRequest(
project="Tutorial Project",
phase_name="On The Road",
event_name="NonExistingEvent",
)
)
pytest.fail("No exception raised for server error response")
except Exception as e:
assert isinstance(e, SherlockDeleteError)

# valid request with actual event
response = lifecycle.delete_event(
DeleteEventRequest(
project="Tutorial Project",
phase_name=phase_name,
event_name=event_name,
)
)
assert response.value == 0


def helper_test_delete_phase(lifecycle: Lifecycle, phase_name: str):
# project missing
try:
lifecycle.delete_phase(
DeletePhaseRequest(
project="",
phase_name="SomePhase",
)
)
pytest.fail("No exception raised when using a missing project parameter")
except Exception as e:
assert isinstance(e, pydantic.ValidationError)
assert str(e.errors()[0]["msg"]) == (
"Value error, project is invalid because it is None or empty."
)

# phase_name missing
try:
lifecycle.delete_phase(
DeletePhaseRequest(
project="Tutorial Project",
phase_name="",
)
)
pytest.fail("No exception raised when using a missing phase_name parameter")
except Exception as e:
assert isinstance(e, pydantic.ValidationError)
assert str(e.errors()[0]["msg"]) == (
"Value error, phase_name is invalid because it is None or empty."
)

if lifecycle._is_connection_up():
# valid request but false phase_name
try:
lifecycle.delete_phase(
DeletePhaseRequest(
project="Tutorial Project",
phase_name="NonExistingPhase",
)
)
pytest.fail("No exception raised for server error response")
except Exception as e:
assert isinstance(e, SherlockDeleteError)

# valid request with actual phase
response = lifecycle.delete_phase(
DeletePhaseRequest(
project="Tutorial Project",
phase_name=phase_name,
)
)
assert response.value == 0


if __name__ == "__main__":
test_all()
Loading