From 4ecf904325a2a4bd3f6a968ee556536214202be5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 May 2026 10:47:37 +0000 Subject: [PATCH 1/3] Initial plan From 28d3a27e607c26a8864ca75e37f73d05aa58269a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 20 May 2026 11:00:05 +0000 Subject: [PATCH 2/3] fix(cli): handle standardized sensors_to_show in show asset output Agent-Logs-Url: https://github.com/FlexMeasures/flexmeasures/sessions/a4320259-a93e-4686-a2e6-4ea40a5876cd Co-authored-by: BelhsanHmida <149331360+BelhsanHmida@users.noreply.github.com> --- flexmeasures/cli/data_show.py | 43 ++++++++++++++++++++---- flexmeasures/cli/tests/test_data_show.py | 38 +++++++++++++++++++++ 2 files changed, 75 insertions(+), 6 deletions(-) diff --git a/flexmeasures/cli/data_show.py b/flexmeasures/cli/data_show.py index b15312eb4f..0b77b8d7f7 100644 --- a/flexmeasures/cli/data_show.py +++ b/flexmeasures/cli/data_show.py @@ -246,12 +246,7 @@ def show_generic_asset(asset): ( asset.generic_asset_type.name, asset.location, - "".join( - [ - f"{graph['title']}: {graph['sensors']} \n" - for graph in standardized_sensors_to_show - ] - ), + _format_sensors_to_show(standardized_sensors_to_show), "".join([f"{k}: {v}\n" for k, v in asset.attributes.items()]), asset.external_id, ) @@ -330,6 +325,42 @@ def show_generic_asset(asset): ) +def _format_sensors_to_show(standardized_sensors_to_show: list[dict]) -> str: + formatted_graphs = [] + for graph in standardized_sensors_to_show: + if not isinstance(graph, dict): + formatted_graphs.append(f"{graph} \n") + continue + title = graph.get("title") or "No Title" + formatted_plots = [_format_sensor_plot(plot) for plot in graph.get("plots", [])] + if not formatted_plots: + formatted_plots = ["[]"] + formatted_graphs.append(f"{title}: {', '.join(formatted_plots)} \n") + return "".join(formatted_graphs) + + +def _format_sensor_plot(plot: dict) -> str: + if not isinstance(plot, dict): + return str(plot) + if "sensor" in plot: + return str(plot["sensor"]) + if "sensors" in plot: + return str(plot["sensors"]) + if "asset" in plot: + asset_text = f"asset={plot['asset']}" + reference_keys = [ + f"{key}={plot[key]}" + for key in ("flex-model", "flex-context") + if key in plot + ] + return ( + f"{asset_text} ({', '.join(reference_keys)})" + if reference_keys + else asset_text + ) + return str(plot) + + @fm_show_data.command("data-sources") @with_appcontext @click.option( diff --git a/flexmeasures/cli/tests/test_data_show.py b/flexmeasures/cli/tests/test_data_show.py index 742d2b3d0e..6ea8030dd2 100644 --- a/flexmeasures/cli/tests/test_data_show.py +++ b/flexmeasures/cli/tests/test_data_show.py @@ -86,6 +86,44 @@ def test_show_asset(app, fresh_db, setup_generic_assets_fresh_db): assert result.exit_code == 1 # command raises a click.Abort Exception +def test_show_asset_with_standardized_sensors_to_show( + app, fresh_db, setup_generic_assets_fresh_db +): + from flexmeasures.cli.data_show import show_generic_asset + + asset = setup_generic_assets_fresh_db["test_wind_turbine"] + asset.sensors_to_show = [{"title": "Power", "plots": [{"sensors": [432, 433]}]}] + fresh_db.session.flush() + + runner = app.test_cli_runner() + result = runner.invoke(show_generic_asset, ["--id", asset.id]) + + assert "Power: [432, 433]" in result.output + assert "KeyError" not in result.output + assert result.exit_code == 1 # command raises a click.Abort Exception + + +def test_format_sensors_to_show_supports_asset_plots(): + from flexmeasures.cli.data_show import _format_sensors_to_show + + formatted_sensors_to_show = _format_sensors_to_show( + [ + { + "title": "Storage", + "plots": [ + {"asset": 12, "flex-model": "soc-min"}, + {"asset": 13, "flex-context": "consumption-price"}, + {"unexpected": "plot"}, + ], + } + ] + ) + + assert "Storage: asset=12 (flex-model=soc-min)" in formatted_sensors_to_show + assert "asset=13 (flex-context=consumption-price)" in formatted_sensors_to_show + assert "{'unexpected': 'plot'}" in formatted_sensors_to_show + + def test_show_forecasters(app, db): from flexmeasures.cli.data_show import list_forecasters From c9df6b04774d9df3917f1b9ea23d5283dd1befb6 Mon Sep 17 00:00:00 2001 From: Mohamed Belhsan Hmida Date: Thu, 21 May 2026 18:07:23 +0100 Subject: [PATCH 3/3] docs: add changelog entry Signed-off-by: Mohamed Belhsan Hmida --- documentation/changelog.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/documentation/changelog.rst b/documentation/changelog.rst index 12ec5e4364..82a7a7495a 100644 --- a/documentation/changelog.rst +++ b/documentation/changelog.rst @@ -35,6 +35,7 @@ Bugfixes * Check read permissions for sensors referenced in forecasting and scheduling config payloads, and return a clearer 403 error when a referenced sensor is not readable [see `PR #2096 `_ and `PR #2125 `_] * Standardize resolution formatting across API endpoints for consistent response payloads [see `PR #2152 `_] * Make the auth check for CLI commands work with ``flask``, too, instead of only with the ``flexmeasures`` alias [see `PR #2169 `_] +* Fix ``flexmeasures show asset`` for assets whose ``sensors_to_show`` uses the standardized ``plots`` schema [see `PR #2189 `_] v0.32.3 | May 15, 2026