Skip to content

Commit 0daf927

Browse files
committed
Add SolarAnalysisData dataclass and clean run_workflow return structure
Introduce a `SolarAnalysisData` dataclass to structure the output of the `run_workflow()` function. Predefine all expected output variables to prevent `UnboundLocalError` in case microgrid data is missing. Signed-off-by: cyiallou - Costas <[email protected]>
1 parent 543fcb7 commit 0daf927

File tree

2 files changed

+67
-15
lines changed

2 files changed

+67
-15
lines changed

RELEASE_NOTES.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
## New Features
1111

12-
<!-- Here goes the main new features and examples or instructions on how to use them -->
12+
- Introduced a `SolarAnalysisData` dataclass to structure the output of the `solar_maintenance_app.run_workflow()` function. This introduces a breaking change in the `Solar Maintenance.ipynb` notebook.
1313

1414
## Bug Fixes
1515

src/frequenz/lib/notebooks/solar/maintenance/solar_maintenance_app.py

Lines changed: 66 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import datetime
1818
import os
19+
from dataclasses import dataclass, field
1920
from typing import TYPE_CHECKING, Any
2021

2122
import matplotlib.pyplot as plt
@@ -66,10 +67,60 @@
6667
SeriesFloat = pd.Series # Treated generically at runtime.
6768

6869

70+
@dataclass
71+
class SolarAnalysisData:
72+
"""Structured output of the Solar Maintenance workflow."""
73+
74+
real_time_view: dict[int, pd.DataFrame] = field(
75+
default_factory=dict,
76+
metadata={"description": "Plot data for real-time view, per microgrid ID."},
77+
)
78+
79+
rolling_view_short_term: dict[int, pd.DataFrame] = field(
80+
default_factory=dict,
81+
metadata={
82+
"description": "Plot data for short-term rolling view, per microgrid ID."
83+
},
84+
)
85+
86+
rolling_view_long_term: dict[int, pd.DataFrame] = field(
87+
default_factory=dict,
88+
metadata={
89+
"description": "Plot data for long-term rolling view, per microgrid ID."
90+
},
91+
)
92+
93+
rolling_view_average: dict[int, pd.DataFrame] = field(
94+
default_factory=dict,
95+
metadata={
96+
"description": "Plot data for averaged rolling view, per microgrid ID."
97+
},
98+
)
99+
100+
daily_production: dict[int, pd.DataFrame] = field(
101+
default_factory=dict,
102+
metadata={
103+
"description": "Plot data for daily production view, per microgrid ID."
104+
},
105+
)
106+
107+
statistical_profiles: dict[int, dict[str, pd.DataFrame]] = field(
108+
default_factory=dict,
109+
metadata={
110+
"description": "Plot data for statistical profiles, per microgrid ID."
111+
},
112+
)
113+
114+
production_table_view: pd.DataFrame | None = field(
115+
default=None,
116+
metadata={
117+
"description": "Table data for production statistics, per microgrid ID.",
118+
},
119+
)
120+
121+
69122
# pylint: disable=too-many-statements, too-many-branches, too-many-locals
70-
async def run_workflow(
71-
user_config_changes: dict[str, Any],
72-
) -> dict[str, pd.DataFrame | dict[str, pd.DataFrame]]:
123+
async def run_workflow(user_config_changes: dict[str, Any]) -> SolarAnalysisData:
73124
"""Run the Solar Maintenance App workflow.
74125
75126
This function fetches and processes the necessary data, generates production
@@ -79,8 +130,7 @@ async def run_workflow(
79130
user_config_changes: A dictionary of user configuration changes.
80131
81132
Returns:
82-
A dictionary containing the data for the plots and the production
83-
statistics table.
133+
A SolarAnalysisData object containing the plot data of the workflow.
84134
85135
Raises:
86136
ValueError:
@@ -192,6 +242,8 @@ async def run_workflow(
192242
"x_axis_label": "x-axis",
193243
"verbose": config.verbose,
194244
}
245+
# initialize the output data structure
246+
output = SolarAnalysisData()
195247
production_table_view_list = []
196248
# pylint: disable-next=too-many-nested-blocks
197249
for mid in reporting_data.microgrid_id.unique():
@@ -746,21 +798,21 @@ async def run_workflow(
746798
for fig in figures_and_axes.keys():
747799
plot_manager.adjust_axes_spacing(fig_id=fig, pixels=100.0)
748800

801+
output.real_time_view[mid] = rolling_view_real_time
802+
output.rolling_view_short_term[mid] = rolling_view_short_term
803+
output.rolling_view_long_term[mid] = rolling_view_long_term
804+
output.rolling_view_average[mid] = rolling_view_average
805+
output.daily_production[mid] = daily_production_view
806+
output.statistical_profiles[mid] = statistical_view
807+
749808
if production_table_view_list:
750809
production_table_view = pd.concat(production_table_view_list, axis=0)
751810
production_table_view.reset_index(inplace=True)
752811
style_table(production_table_view, show=True)
753812
else:
754813
production_table_view = pd.DataFrame()
755-
return {
756-
"real_time_view": rolling_view_real_time,
757-
"rolling_view_short_term": rolling_view_short_term,
758-
"rolling_view_long_term": rolling_view_long_term,
759-
"rolling_view_average": rolling_view_average,
760-
"daily_production": daily_production_view,
761-
"statistical_profiles": statistical_view,
762-
"production_statistics_table": production_table_view,
763-
}
814+
output.production_table_view = production_table_view
815+
return output
764816

765817

766818
def _load_and_validate_config(

0 commit comments

Comments
 (0)