Skip to content
Open
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
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM python:3.9.6
FROM python:3.9.25-bookworm

# Do not run as root
RUN adduser dev
Expand Down
10 changes: 9 additions & 1 deletion docs/source/spelling_wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,12 @@ userspace
sys
netloc
StartTime
StopTime
StopTime
Orchestrator
orchestrator
attrib
dir
parametrization
parametrized
str
workspaces
2 changes: 1 addition & 1 deletion modelon/impact/client/entities/experiment.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def successful(self) -> int:

@property
def failed(self) -> int:
"""Number of cases in experiment thar have failed."""
"""Number of cases in experiment that have failed."""
return self._failed

@property
Expand Down
119 changes: 98 additions & 21 deletions modelon/impact/client/entities/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import logging
import os
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Union
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Union

from modelon.impact.client.configuration import Experimental
from modelon.impact.client.entities.custom_function import (
Expand Down Expand Up @@ -376,12 +376,12 @@ def update_experiment_definition(
experiment_definition_id: str,
definition: SimpleModelicaExperimentDefinition,
name: Optional[str] = None,
is_default: bool = False,
is_default: Optional[bool] = None,
) -> ExperimentDefinitionEntry:
"""Updates an existing experiment definition for this model.

The stored definition is replaced with the given one, including its
metadata (name and default flag).
The stored experiment is replaced with the given one. Metadata the
caller leaves out is kept as it is.

Requires an active modeling session, since a definition inherited from
a parent class via 'extends' can only be recognized as read-only by
Expand All @@ -394,23 +394,105 @@ def update_experiment_definition(
name: New name for the experiment definition. Default: None, which
keeps the existing name unchanged.
is_default: Whether to mark this experiment definition as the
default for the model. Default: False.
default for the model. Default: None, which keeps the existing
default flag unchanged.

Example::

with workspace.new_modeling_session() as session:
model = session.get_model("LibA.Model")
entry = model.get_experiment_definitions()[0]
definition = entry.definition.with_modifiers({'inertia1.J': 2})
updated = model.update_experiment_definition(
entry.id, definition, is_default=entry.is_default
updated = model.update_experiment_definition(entry.id, definition)

Raises:
ExperimentDefinitionReadOnlyError: If the experiment definition is
read-only, e.g. because it belongs to a read-only project or is
inherited from a parent class via 'extends'.

"""
name, is_default = self._experiment_definition_metadata_to_keep(
experiment_definition_id, name, is_default
)
resp = self._sal.project.experiment_definition_update(
self._project_id,
self._class_name,
experiment_definition_id,
name=name,
experiment=_to_editable_experiment_content(definition),
is_default=is_default,
)
return self._experiment_definition_entry_from_item(resp["data"])

@Experimental
def update_experiment_definition_metadata(
self,
experiment_definition_id: str,
name: Optional[str] = None,
is_default: Optional[bool] = None,
) -> ExperimentDefinitionEntry:
"""Updates the metadata of an existing experiment definition, leaving the stored
experiment itself untouched.

Use this to rename a definition, or to mark it as the model's default,
without having to fetch and re-send the whole experiment.

Requires an active modeling session, since a definition inherited from
a parent class via 'extends' can only be recognized as read-only by
resolving the model's extends clauses.

Args:
experiment_definition_id: The ID of the experiment definition to
update, e.g. from get_experiment_definitions().
name: New name for the experiment definition. Default: None, which
keeps the existing name unchanged.
is_default: Whether to mark this experiment definition as the
default for the model. Default: None, which keeps the existing
default flag unchanged.

Example::

with workspace.new_modeling_session() as session:
model = session.get_model("LibA.Model")
entry = model.get_experiment_definitions()[0]
renamed = model.update_experiment_definition_metadata(
entry.id, name="My experiment"
)

Raises:
ExperimentDefinitionReadOnlyError: If the experiment definition is
read-only, e.g. because it belongs to a read-only project or is
inherited from a parent class via 'extends'.

"""
name, is_default = self._experiment_definition_metadata_to_keep(
experiment_definition_id, name, is_default
)
resp = self._sal.project.experiment_definition_metadata_update(
self._project_id,
self._class_name,
experiment_definition_id,
name=name,
is_default=is_default,
)
return self._experiment_definition_entry_from_item(resp["data"])

def _experiment_definition_metadata_to_keep(
self,
experiment_definition_id: str,
name: Optional[str],
is_default: Optional[bool],
) -> Tuple[str, bool]:
"""Resolves the metadata to send for an update of an experiment definition of
this model.

Both endpoints replace the whole metadata, so whatever the caller left
out is filled in from the stored definition to keep it as it is.

Raises:
ExperimentDefinitionReadOnlyError: If the experiment definition is
read-only.

"""
metadata = self._get_experiment_definition_metadata(experiment_definition_id)
if metadata is not None and metadata["isReadOnly"]:
Expand All @@ -419,23 +501,18 @@ def update_experiment_definition(
"read-only and cannot be updated. It belongs to a read-only "
"project or is inherited from a parent class via 'extends'."
)
if name is None:
if name is None or is_default is None:
if metadata is None:
raise ValueError(
"Could not resolve the current name of experiment "
f"definition '{experiment_definition_id}'; pass 'name' "
"explicitly."
"Could not resolve the current metadata of experiment "
f"definition '{experiment_definition_id}'; pass 'name' and "
"'is_default' explicitly."
)
name = metadata["name"]
resp = self._sal.project.experiment_definition_update(
self._project_id,
self._class_name,
experiment_definition_id,
name=name,
experiment=_to_editable_experiment_content(definition),
is_default=is_default,
)
return self._experiment_definition_entry_from_item(resp["data"])
if name is None:
name = metadata["name"]
if is_default is None:
is_default = metadata["isDefault"]
return name, is_default

def _get_experiment_definition_metadata(
self, experiment_definition_id: str
Expand Down
19 changes: 17 additions & 2 deletions modelon/impact/client/sal/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def experiment_definition_create(
).resolve()
body = {
"data": {
"metadata": {"name": name, "isDefault": is_default},
"metadata": {"name": name, "setAsDefault": is_default},
"experiment": experiment,
}
}
Expand All @@ -80,12 +80,27 @@ def experiment_definition_update(
).resolve()
body = {
"data": {
"metadata": {"name": name, "isDefault": is_default},
"metadata": {"name": name, "setAsDefault": is_default},
"experiment": experiment,
}
}
return self._http_client.put_json(url, body=body)

def experiment_definition_metadata_update(
self,
project_id: str,
model_path: str,
experiment_definition_id: str,
name: str,
is_default: bool = False,
) -> Dict[str, Any]:
url = (
self._base_uri / f"api/projects/{project_id}/models/{model_path}/"
f"experiment-definitions/{experiment_definition_id}/metadata"
).resolve()
body = {"data": {"name": name, "setAsDefault": is_default}}
return self._http_client.put_json(url, body=body)

def project_options_get(
self, project_id: str, workspace_id: str, custom_function: str
) -> Dict[str, Any]:
Expand Down