diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f95d6b9..6134047d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,13 @@ -<<<<<<< HEAD ### Upcoming - - improvements to the real-time profile editor, - - `hours_elapsed()` is a function in profile expressions. - - Breaking change: `log` in experiment profiles now uses expressions. + - improvements to the UI's experiment profile preview. It's so pretty! + - `hours_elapsed()` is a function in profile expressions, which returns the hours since the profile started. + - `unit()` can be used in mqtt fetch expressions. Example: `unit():stirring:target_rpm` is identical to `::stirring:target_rpm`. The latter can be seen as a shortened version of the former. + - Breaking change: `log` in experiment profiles now uses expressions instead of python formatting. For example: `The unit {unit} is running {job} in experiment {experiment}` should be replaced by expressions in the string: `The unit ${{unit()}} is running ${{job_name()}} in the experiment ${{experiment}}`. - Updated Raspberry Pi OS image to 2024-07-04. -### 24.7.5 & 24.7.6 -======= + ### 24.7.5 & 24.7.6 & 24.7.7 ->>>>>>> master Hotfix release for 24.7.3. This pins blinka to a specific version which does not install numpy. diff --git a/pioreactor/experiment_profiles/profile_struct.py b/pioreactor/experiment_profiles/profile_struct.py index 457076a0..c101b672 100644 --- a/pioreactor/experiment_profiles/profile_struct.py +++ b/pioreactor/experiment_profiles/profile_struct.py @@ -92,10 +92,11 @@ def __str__(self) -> str: class Job(Struct, forbid_unknown_fields=True): actions: list[Action] - # description? + description: Optional[str] = None # metadata? # calibration_settings? # config_options? + # logging? PioreactorUnitName = str diff --git a/pioreactor/tests/test_execute_experiment_profile.py b/pioreactor/tests/test_execute_experiment_profile.py index 2c7a8cc0..550e1ab1 100644 --- a/pioreactor/tests/test_execute_experiment_profile.py +++ b/pioreactor/tests/test_execute_experiment_profile.py @@ -604,6 +604,50 @@ def collection_actions(msg): ) +@patch("pioreactor.actions.leader.experiment_profile._load_experiment_profile") +def test_execute_experiment_profile_expression_in_common_also_works_with_unit_function( + mock__load_experiment_profile, active_workers_in_cluster +) -> None: + job_name = "jobbing" + + for worker in active_workers_in_cluster: + publish(f"pioreactor/{worker}/_testing_experiment/{job_name}/target", 10, retain=True) + + action = Start( + hours_elapsed=0, options={"target": "${{unit():jobbing:target + 1}}"}, if_="unit():jobbing:target > 0" + ) + + profile = Profile( + experiment_profile_name="test_profile", + plugins=[], + common=CommonBlock( + jobs={ + job_name: Job(actions=[action]), + } + ), + metadata=Metadata(author="test_author"), + ) + + mock__load_experiment_profile.return_value = profile + + actions = [] + + def collection_actions(msg): + actions.append(msg.payload.decode()) + + subscribe_and_callback( + collection_actions, + [f"pioreactor/{worker}/_testing_experiment/run/jobbing" for worker in active_workers_in_cluster], + allow_retained=False, + ) + + execute_experiment_profile("profile.yaml", "_testing_experiment") + + assert actions == ['{"options":{"target":11.0,"job_source":"experiment_profile"},"args":[]}'] * len( + active_workers_in_cluster + ) + + @patch("pioreactor.actions.leader.experiment_profile._load_experiment_profile") def test_execute_experiment_profile_when_action_simple(mock__load_experiment_profile) -> None: experiment = "_testing_experiment"