frequenz-cs-reporting 0.4.4__tar.gz → 0.4.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/PKG-INFO +1 -1
- frequenz_cs_reporting-0.4.5/RELEASE_NOTES.md +16 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/app_pages/reporting.py +20 -1
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/components/tables.py +70 -26
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/services/data_service.py +75 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/views/dashboard.py +5 -7
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/views/plot_renderers.py +91 -24
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/views/table_renderers.py +0 -72
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz_cs_reporting.egg-info/PKG-INFO +1 -1
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz_cs_reporting.egg-info/scm_file_list.json +68 -68
- frequenz_cs_reporting-0.4.5/src/frequenz_cs_reporting.egg-info/scm_version.json +8 -0
- frequenz_cs_reporting-0.4.4/RELEASE_NOTES.md +0 -21
- frequenz_cs_reporting-0.4.4/src/frequenz_cs_reporting.egg-info/scm_version.json +0 -8
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/.streamlit/config.toml +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/Dockerfile +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/LICENSE +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/MANIFEST.in +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/README.md +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/app.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/pyproject.toml +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/setup.cfg +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/__init__.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/app_pages/__init__.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/app_pages/home.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/app_pages/solar.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/assets/__init__.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/assets/neustrom_background.png +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/assets/neustrom_logo.png +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/components/__init__.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/components/inputs.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/components/plot_charts.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/components/sidebar_inputs.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/components/ui.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/conftest.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/constants.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/py.typed +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/rep_cs_core/__init__.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/rep_cs_core/config.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/rep_cs_core/page_spec.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/services/__init__.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/services/client_factory.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/services/solar_workflow.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/styles/dashboard.css +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/styles/home.css +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/styles/kpi.css +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/styles/plot_card.css +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/templates/dashboard_section_divider.html +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/templates/home_feature_grid.html +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/templates/home_feature_item.html +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/templates/home_footer.html +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/templates/home_hero.html +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/templates/home_logo.html +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/templates/home_stat_item.html +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/templates/home_stats.html +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/ui_resources.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/utils/__init__.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/utils/env.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/utils/time.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/views/__init__.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/views/metric_renderers.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/views/sections.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/views/solar_results.py +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz_cs_reporting.egg-info/SOURCES.txt +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz_cs_reporting.egg-info/dependency_links.txt +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz_cs_reporting.egg-info/requires.txt +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz_cs_reporting.egg-info/top_level.txt +0 -0
- {frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/toml_directory/__init__.py +0 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Frequenz CS Reporting Library Release Notes
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Upgrading
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## New Features
|
|
10
|
+
|
|
11
|
+
- Fetch and cache battery state-of-charge data for microgrids with batteries.
|
|
12
|
+
- Add a dedicated "Batterie SOC" plot tab showing battery power flow and state of charge.
|
|
13
|
+
|
|
14
|
+
## Bug Fixes
|
|
15
|
+
- Update the table filtering logic to work in deepnote.
|
|
16
|
+
|
|
@@ -22,7 +22,10 @@ from frequenz.cs_reporting.services.client_factory import (
|
|
|
22
22
|
get_component_types,
|
|
23
23
|
get_microgrid_config,
|
|
24
24
|
)
|
|
25
|
-
from frequenz.cs_reporting.services.data_service import
|
|
25
|
+
from frequenz.cs_reporting.services.data_service import (
|
|
26
|
+
get_microgrid_data,
|
|
27
|
+
get_microgrid_soc_data,
|
|
28
|
+
)
|
|
26
29
|
from frequenz.cs_reporting.views.dashboard import build_master_df, render_dashboard
|
|
27
30
|
|
|
28
31
|
|
|
@@ -206,6 +209,21 @@ def render() -> None:
|
|
|
206
209
|
end_date=end_time,
|
|
207
210
|
resolution=resolution,
|
|
208
211
|
)
|
|
212
|
+
battery_soc_df = None
|
|
213
|
+
if "battery" in component_types:
|
|
214
|
+
try:
|
|
215
|
+
fetched_soc_df = get_microgrid_soc_data(
|
|
216
|
+
microgrid_id=microgrid_id,
|
|
217
|
+
start_date=start_time,
|
|
218
|
+
end_date=end_time,
|
|
219
|
+
resolution=resolution,
|
|
220
|
+
)
|
|
221
|
+
except Exception as exc: # pylint: disable=broad-except
|
|
222
|
+
st.warning(
|
|
223
|
+
f"Batterie-SOC-Daten konnten nicht geladen werden: {exc}"
|
|
224
|
+
)
|
|
225
|
+
else:
|
|
226
|
+
battery_soc_df = None if fetched_soc_df.empty else fetched_soc_df
|
|
209
227
|
except Exception as exc: # pylint: disable=broad-except
|
|
210
228
|
st.error(_format_data_loading_error(exc, microgrid_id))
|
|
211
229
|
st.stop()
|
|
@@ -226,6 +244,7 @@ def render() -> None:
|
|
|
226
244
|
mcfg,
|
|
227
245
|
mapper,
|
|
228
246
|
timezone=timezone,
|
|
247
|
+
battery_soc_df=battery_soc_df,
|
|
229
248
|
)
|
|
230
249
|
render_dashboard(
|
|
231
250
|
master_df,
|
|
@@ -19,6 +19,7 @@ _GRID_HEADER_HEIGHT = 40
|
|
|
19
19
|
_GRID_ROW_HEIGHT = 44
|
|
20
20
|
_GRID_PAGINATION_HEIGHT = 56
|
|
21
21
|
_GRID_BORDER_HEIGHT = 2
|
|
22
|
+
_GRID_TOOLBAR_HEIGHT = 40
|
|
22
23
|
|
|
23
24
|
|
|
24
25
|
# pylint: disable=too-many-arguments
|
|
@@ -90,39 +91,82 @@ def aggrid_table(
|
|
|
90
91
|
suppressAutoSize=True,
|
|
91
92
|
)
|
|
92
93
|
|
|
93
|
-
# Fit columns on first load
|
|
94
94
|
grid_options = gb.build()
|
|
95
95
|
grid_options["onGridReady"] = JsCode(f"""
|
|
96
96
|
function(params) {{
|
|
97
|
-
const
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const previousHandler =
|
|
103
|
-
window.__csReportingAgGridResetHandlers[gridKey];
|
|
104
|
-
if (previousHandler) {{
|
|
105
|
-
window.removeEventListener("message", previousHandler);
|
|
97
|
+
const buttonId = {json.dumps(f"{key_prefix}_reset_table_filters")};
|
|
98
|
+
const toolbarHeight = {_GRID_TOOLBAR_HEIGHT};
|
|
99
|
+
const existingButton = document.getElementById(buttonId);
|
|
100
|
+
if (existingButton) {{
|
|
101
|
+
return;
|
|
106
102
|
}}
|
|
107
103
|
|
|
108
|
-
const
|
|
104
|
+
const gridRoot = document.querySelector(".ag-root-wrapper");
|
|
105
|
+
const gridParent = gridRoot ? gridRoot.parentElement : null;
|
|
106
|
+
if (!gridRoot || !gridParent) {{
|
|
107
|
+
return;
|
|
108
|
+
}}
|
|
109
|
+
|
|
110
|
+
const currentGridHeight = gridRoot.getBoundingClientRect().height;
|
|
111
|
+
if (currentGridHeight > toolbarHeight) {{
|
|
112
|
+
gridRoot.style.height = `${{currentGridHeight - toolbarHeight}}px`;
|
|
113
|
+
}}
|
|
114
|
+
|
|
115
|
+
const toolbar = document.createElement("div");
|
|
116
|
+
Object.assign(toolbar.style, {{
|
|
117
|
+
alignItems: "center",
|
|
118
|
+
backgroundColor: "transparent",
|
|
119
|
+
boxSizing: "border-box",
|
|
120
|
+
display: "flex",
|
|
121
|
+
height: `${{toolbarHeight}}px`,
|
|
122
|
+
justifyContent: "flex-start",
|
|
123
|
+
padding: "4px 0 6px 0",
|
|
124
|
+
}});
|
|
125
|
+
|
|
126
|
+
const button = document.createElement("button");
|
|
127
|
+
button.id = buttonId;
|
|
128
|
+
button.type = "button";
|
|
129
|
+
button.textContent = "Filter zurücksetzen";
|
|
130
|
+
button.title = "Filter der Tabelle zurücksetzen";
|
|
131
|
+
button.setAttribute("aria-label", "Filter der Tabelle zurücksetzen");
|
|
132
|
+
|
|
133
|
+
Object.assign(button.style, {{
|
|
134
|
+
backgroundColor: "#fff4bf",
|
|
135
|
+
border: "1px solid #e4c34a",
|
|
136
|
+
color: "#4f3f00",
|
|
137
|
+
borderRadius: "6px",
|
|
138
|
+
cursor: "pointer",
|
|
139
|
+
fontFamily: "inherit",
|
|
140
|
+
fontSize: "12px",
|
|
141
|
+
fontWeight: "600",
|
|
142
|
+
lineHeight: "1.1",
|
|
143
|
+
minHeight: "26px",
|
|
144
|
+
padding: "4px 8px",
|
|
145
|
+
whiteSpace: "nowrap",
|
|
146
|
+
}});
|
|
147
|
+
|
|
148
|
+
button.addEventListener("mouseenter", function() {{
|
|
149
|
+
button.style.backgroundColor = "#ffe88a";
|
|
150
|
+
button.style.borderColor = "#c7a629";
|
|
151
|
+
button.style.color = "#3f3200";
|
|
152
|
+
}});
|
|
153
|
+
button.addEventListener("mouseleave", function() {{
|
|
154
|
+
button.style.backgroundColor = "#fff4bf";
|
|
155
|
+
button.style.borderColor = "#e4c34a";
|
|
156
|
+
button.style.color = "#4f3f00";
|
|
157
|
+
}});
|
|
158
|
+
|
|
159
|
+
button.addEventListener("click", function(event) {{
|
|
160
|
+
event.preventDefault();
|
|
161
|
+
event.stopPropagation();
|
|
162
|
+
|
|
109
163
|
params.api.setFilterModel(null);
|
|
110
164
|
params.api.onFilterChanged();
|
|
111
165
|
params.api.paginationGoToFirstPage();
|
|
112
|
-
}};
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
event.data &&
|
|
117
|
-
event.data.type === messageType &&
|
|
118
|
-
event.data.gridKey === gridKey
|
|
119
|
-
) {{
|
|
120
|
-
resetFilters();
|
|
121
|
-
}}
|
|
122
|
-
}};
|
|
123
|
-
|
|
124
|
-
window.__csReportingAgGridResetHandlers[gridKey] = handler;
|
|
125
|
-
window.addEventListener("message", handler);
|
|
166
|
+
}});
|
|
167
|
+
|
|
168
|
+
toolbar.appendChild(button);
|
|
169
|
+
gridParent.insertBefore(toolbar, gridRoot);
|
|
126
170
|
}}
|
|
127
171
|
""")
|
|
128
172
|
|
|
@@ -160,7 +204,7 @@ def aggrid_table(
|
|
|
160
204
|
_ = AgGrid(
|
|
161
205
|
df,
|
|
162
206
|
gridOptions=grid_options,
|
|
163
|
-
height=height,
|
|
207
|
+
height=height + _GRID_TOOLBAR_HEIGHT,
|
|
164
208
|
theme=theme,
|
|
165
209
|
allow_unsafe_jscode=True,
|
|
166
210
|
update_mode=GridUpdateMode.NO_UPDATE,
|
|
@@ -99,6 +99,43 @@ async def fetch_microgrid_data(
|
|
|
99
99
|
return df.copy()
|
|
100
100
|
|
|
101
101
|
|
|
102
|
+
async def fetch_microgrid_soc_data(
|
|
103
|
+
microgrid_id: int,
|
|
104
|
+
start_date: datetime,
|
|
105
|
+
end_date: datetime,
|
|
106
|
+
resolution: timedelta,
|
|
107
|
+
timeout: float = 30.0,
|
|
108
|
+
) -> pd.DataFrame:
|
|
109
|
+
"""Fetch battery SOC for a microgrid in ``[start_date, end_date)``.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
microgrid_id: Identifier for the target microgrid.
|
|
113
|
+
start_date: Inclusive start date of the query range.
|
|
114
|
+
end_date: Exclusive end date of the query range.
|
|
115
|
+
resolution: Resampling period for the returned data.
|
|
116
|
+
timeout: Request timeout in seconds.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
Dataframe of battery SOC values. Empty when no SOC data is available.
|
|
120
|
+
"""
|
|
121
|
+
start_iso, end_iso = validate_range(start_date, end_date)
|
|
122
|
+
client = get_microgrid_client(microgrid_id)
|
|
123
|
+
|
|
124
|
+
coro = client.soc(
|
|
125
|
+
microgrid_id=microgrid_id,
|
|
126
|
+
start=start_iso,
|
|
127
|
+
end=end_iso,
|
|
128
|
+
resampling_period=resolution,
|
|
129
|
+
keep_components=False,
|
|
130
|
+
)
|
|
131
|
+
with _quiet_expected_component_data_warnings():
|
|
132
|
+
df = await asyncio.wait_for(coro, timeout)
|
|
133
|
+
|
|
134
|
+
if df is None or (hasattr(df, "empty") and df.empty):
|
|
135
|
+
return pd.DataFrame()
|
|
136
|
+
return df.copy()
|
|
137
|
+
|
|
138
|
+
|
|
102
139
|
# Cached sync wrapper for Streamlit pages
|
|
103
140
|
@st.cache_data(ttl=300, show_spinner=False)
|
|
104
141
|
def get_microgrid_data(
|
|
@@ -139,3 +176,41 @@ def get_microgrid_data(
|
|
|
139
176
|
"get_microgrid_data() called from within an active event loop. "
|
|
140
177
|
"Use `await fetch_microgrid_data(...)` in async contexts."
|
|
141
178
|
)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
@st.cache_data(ttl=300, show_spinner=False)
|
|
182
|
+
def get_microgrid_soc_data(
|
|
183
|
+
microgrid_id: int,
|
|
184
|
+
start_date: datetime,
|
|
185
|
+
end_date: datetime,
|
|
186
|
+
resolution: timedelta,
|
|
187
|
+
timeout: float = 30.0,
|
|
188
|
+
) -> pd.DataFrame:
|
|
189
|
+
"""Sync wrapper for battery SOC data with caching.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
microgrid_id: Identifier for the target microgrid.
|
|
193
|
+
start_date: Inclusive start date of the query range.
|
|
194
|
+
end_date: Exclusive end date of the query range.
|
|
195
|
+
resolution: Resampling period for the returned data.
|
|
196
|
+
timeout: Request timeout in seconds.
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
Dataframe of battery SOC values. Empty when no SOC data is available.
|
|
200
|
+
|
|
201
|
+
Raises:
|
|
202
|
+
RuntimeError: If invoked from within an active event loop instead of
|
|
203
|
+
using the async ``fetch_microgrid_soc_data``.
|
|
204
|
+
"""
|
|
205
|
+
try:
|
|
206
|
+
asyncio.get_running_loop()
|
|
207
|
+
except RuntimeError:
|
|
208
|
+
return asyncio.run(
|
|
209
|
+
fetch_microgrid_soc_data(
|
|
210
|
+
microgrid_id, start_date, end_date, resolution, timeout=timeout
|
|
211
|
+
)
|
|
212
|
+
)
|
|
213
|
+
raise RuntimeError(
|
|
214
|
+
"get_microgrid_soc_data() called from within an active event loop. "
|
|
215
|
+
"Use `await fetch_microgrid_soc_data(...)` in async contexts."
|
|
216
|
+
)
|
|
@@ -105,12 +105,6 @@ def _build_tables(
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
overview_df = build_overview_df(master_df, component_types)
|
|
108
|
-
if "day_ahead_price" in master_df.columns and "timestamp" in overview_df.columns:
|
|
109
|
-
overview_df = overview_df.merge(
|
|
110
|
-
master_df[["timestamp", "day_ahead_price"]],
|
|
111
|
-
on="timestamp",
|
|
112
|
-
how="left",
|
|
113
|
-
)
|
|
114
108
|
|
|
115
109
|
return {
|
|
116
110
|
"power_table": power_table,
|
|
@@ -124,12 +118,14 @@ def _build_tables(
|
|
|
124
118
|
}
|
|
125
119
|
|
|
126
120
|
|
|
121
|
+
# pylint: disable=too-many-arguments, too-many-positional-arguments
|
|
127
122
|
def build_master_df(
|
|
128
123
|
raw_df: pd.DataFrame,
|
|
129
124
|
component_types: Iterable[str],
|
|
130
125
|
mcfg: Any,
|
|
131
126
|
mapper: ColumnMapper,
|
|
132
127
|
timezone: str = "Europe/Berlin",
|
|
128
|
+
battery_soc_df: pd.DataFrame | None = None,
|
|
133
129
|
) -> pd.DataFrame:
|
|
134
130
|
"""Transform raw microgrid data into master analysis dataframe.
|
|
135
131
|
|
|
@@ -146,6 +142,7 @@ def build_master_df(
|
|
|
146
142
|
site-specific settings.
|
|
147
143
|
mapper: Column name mapper for converting internal names to display names.
|
|
148
144
|
timezone: Timezone name to use for the report timestamps.
|
|
145
|
+
battery_soc_df: Optional battery SOC dataframe to merge into the report.
|
|
149
146
|
|
|
150
147
|
Returns:
|
|
151
148
|
Master dataframe with processed and aggregated component data, ready for
|
|
@@ -171,6 +168,7 @@ def build_master_df(
|
|
|
171
168
|
tz_name=timezone,
|
|
172
169
|
assume_tz="UTC",
|
|
173
170
|
component_display_names=component_display_names,
|
|
171
|
+
battery_soc_df=battery_soc_df,
|
|
174
172
|
)
|
|
175
173
|
try:
|
|
176
174
|
master_df = merge_day_ahead_prices(
|
|
@@ -218,7 +216,7 @@ def render_dashboard(
|
|
|
218
216
|
|
|
219
217
|
# --- Plots section---
|
|
220
218
|
_section_divider("Diagramme & Zeitreihen")
|
|
221
|
-
sections.render_plots_tabs(tables, mapper)
|
|
219
|
+
sections.render_plots_tabs(tables, mapper, component_types)
|
|
222
220
|
|
|
223
221
|
# --- Tables section---
|
|
224
222
|
st.markdown('<div id="data-export-section"></div>', unsafe_allow_html=True)
|
|
@@ -6,23 +6,21 @@
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
8
|
from functools import partial
|
|
9
|
-
from typing import Callable
|
|
9
|
+
from typing import Callable, Iterable
|
|
10
10
|
|
|
11
11
|
import pandas as pd
|
|
12
|
+
import plotly.graph_objects as go
|
|
12
13
|
import streamlit as st
|
|
13
14
|
from frequenz.lib.notebooks.reporting.plotter import (
|
|
14
15
|
plot_energy_pie_chart,
|
|
15
16
|
plot_time_series,
|
|
16
|
-
|
|
17
|
+
plot_time_series_battery_soc,
|
|
18
|
+
plot_time_series_battery_soc_and_usecase,
|
|
17
19
|
)
|
|
18
20
|
from frequenz.lib.notebooks.reporting.utils.column_mapper import ColumnMapper
|
|
19
21
|
|
|
20
22
|
from frequenz.cs_reporting.components.ui import render_plot_card
|
|
21
|
-
from frequenz.cs_reporting.constants import
|
|
22
|
-
COLOR_DICT,
|
|
23
|
-
COMPONENT_CONFIGS,
|
|
24
|
-
TablesResult,
|
|
25
|
-
)
|
|
23
|
+
from frequenz.cs_reporting.constants import COLOR_DICT, COMPONENT_CONFIGS, TablesResult
|
|
26
24
|
|
|
27
25
|
_COMPONENT_TABS = [
|
|
28
26
|
("PV Leistung", "pv"),
|
|
@@ -36,6 +34,12 @@ _BATTERY_OVERVIEW_MARGIN = {"t": 150, "r": 56, "b": 96, "l": 64}
|
|
|
36
34
|
_BATTERY_OVERVIEW_RANGE_SLIDER_THICKNESS = 0.15
|
|
37
35
|
|
|
38
36
|
|
|
37
|
+
def _left_align_plot_title(fig: object) -> None:
|
|
38
|
+
"""Align Plotly figure titles with the left edge of the chart container."""
|
|
39
|
+
if isinstance(fig, go.Figure):
|
|
40
|
+
fig.update_layout(title={"x": 0, "xref": "container", "xanchor": "left"})
|
|
41
|
+
|
|
42
|
+
|
|
39
43
|
# pylint: disable=too-many-arguments
|
|
40
44
|
def render_time_series(
|
|
41
45
|
df: pd.DataFrame,
|
|
@@ -92,7 +96,7 @@ def render_time_series(
|
|
|
92
96
|
df,
|
|
93
97
|
time_col=time_col,
|
|
94
98
|
cols=cols,
|
|
95
|
-
title=
|
|
99
|
+
title="",
|
|
96
100
|
xaxis_title=xaxis_title,
|
|
97
101
|
yaxis_title=yaxis_title,
|
|
98
102
|
legend_title=legend_title,
|
|
@@ -104,6 +108,12 @@ def render_time_series(
|
|
|
104
108
|
dotted_cols=dotted_cols,
|
|
105
109
|
plot_order=plot_order,
|
|
106
110
|
)
|
|
111
|
+
fig.update_layout(
|
|
112
|
+
height=_BATTERY_OVERVIEW_HEIGHT,
|
|
113
|
+
margin=_BATTERY_OVERVIEW_MARGIN,
|
|
114
|
+
xaxis_rangeslider_thickness=_BATTERY_OVERVIEW_RANGE_SLIDER_THICKNESS,
|
|
115
|
+
)
|
|
116
|
+
_left_align_plot_title(fig)
|
|
107
117
|
|
|
108
118
|
render_plot_card(title, fig)
|
|
109
119
|
|
|
@@ -194,64 +204,119 @@ def _prepare_battery_usecase_df(tables: TablesResult) -> pd.DataFrame | None:
|
|
|
194
204
|
Returns:
|
|
195
205
|
Battery-usecase dataframe, or ``None`` when unavailable.
|
|
196
206
|
"""
|
|
197
|
-
|
|
198
|
-
if
|
|
207
|
+
battery_usecase_df = tables.get("overview_df")
|
|
208
|
+
if battery_usecase_df is None or battery_usecase_df.empty:
|
|
199
209
|
return None
|
|
200
210
|
|
|
201
|
-
return
|
|
211
|
+
return battery_usecase_df.drop(columns={"grid_feed_in"}, errors="ignore")
|
|
202
212
|
|
|
203
213
|
|
|
204
|
-
def _render_overview_plot(
|
|
214
|
+
def _render_overview_plot(battery_usecase_df: pd.DataFrame | None) -> None:
|
|
205
215
|
"""Render the main time-series overview plot.
|
|
206
216
|
|
|
207
217
|
Args:
|
|
208
|
-
|
|
218
|
+
battery_usecase_df: Battery-usecase dataframe in canonical naming convention.
|
|
209
219
|
|
|
210
220
|
Returns:
|
|
211
221
|
Streamlit components are rendered directly.
|
|
212
222
|
"""
|
|
213
|
-
if
|
|
223
|
+
if battery_usecase_df is None or battery_usecase_df.empty:
|
|
214
224
|
st.info("Keine Übersichtsdaten zum Plotten verfügbar.")
|
|
215
225
|
return
|
|
216
226
|
|
|
217
|
-
|
|
218
|
-
cols_list = [col for col in overview_df.columns if col not in helper_cols]
|
|
227
|
+
cols_list = battery_usecase_df.columns.tolist()
|
|
219
228
|
secondary_y_cols = (
|
|
220
|
-
["day_ahead_price"] if "day_ahead_price" in
|
|
229
|
+
["day_ahead_price"] if "day_ahead_price" in battery_usecase_df.columns else None
|
|
221
230
|
)
|
|
222
|
-
fig =
|
|
223
|
-
|
|
224
|
-
time_col="timestamp",
|
|
231
|
+
fig = plot_time_series_battery_soc_and_usecase(
|
|
232
|
+
battery_usecase_df,
|
|
225
233
|
cols=cols_list,
|
|
234
|
+
time_col="timestamp",
|
|
235
|
+
battery_power_flow="battery_power_flow",
|
|
236
|
+
soc_pct="battery_soc_pct",
|
|
226
237
|
legend_title=None,
|
|
227
238
|
secondary_y_cols=secondary_y_cols,
|
|
228
|
-
secondary_y_title="EUR/MWh"
|
|
239
|
+
secondary_y_title="EUR/MWh",
|
|
229
240
|
title="",
|
|
230
241
|
dotted_cols=[
|
|
231
242
|
"grid_consumption_without_battery",
|
|
232
243
|
"peak_before_optimization",
|
|
233
244
|
"day_ahead_price",
|
|
234
245
|
],
|
|
235
|
-
stack_mode="
|
|
246
|
+
stack_mode="energy_balance",
|
|
247
|
+
xaxis_title="Zeitpunkt",
|
|
248
|
+
yaxis_title="kW",
|
|
249
|
+
soc_secondary_y_title="SOC [%]",
|
|
236
250
|
)
|
|
237
251
|
fig.update_layout(
|
|
238
252
|
height=_BATTERY_OVERVIEW_HEIGHT,
|
|
239
253
|
margin=_BATTERY_OVERVIEW_MARGIN,
|
|
240
254
|
xaxis_rangeslider_thickness=_BATTERY_OVERVIEW_RANGE_SLIDER_THICKNESS,
|
|
241
255
|
)
|
|
256
|
+
_left_align_plot_title(fig)
|
|
242
257
|
render_plot_card("Lastgang Übersicht", fig)
|
|
243
258
|
|
|
244
259
|
|
|
260
|
+
def _render_battery_soc_plot(battery_usecase_df: pd.DataFrame | None) -> None:
|
|
261
|
+
"""Render the battery power flow and SOC plot.
|
|
262
|
+
|
|
263
|
+
Args:
|
|
264
|
+
battery_usecase_df: Battery-usecase dataframe in canonical naming convention.
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
Streamlit components are rendered directly.
|
|
268
|
+
"""
|
|
269
|
+
if battery_usecase_df is None or battery_usecase_df.empty:
|
|
270
|
+
st.info("Keine Batteriedaten zum Plotten verfügbar.")
|
|
271
|
+
return
|
|
272
|
+
|
|
273
|
+
required_cols = {"timestamp", "battery_power_flow", "battery_soc_pct"}
|
|
274
|
+
missing_cols = required_cols.difference(battery_usecase_df.columns)
|
|
275
|
+
if missing_cols:
|
|
276
|
+
st.info("Keine gültigen Batteriedaten für den SOC-Plot verfügbar.")
|
|
277
|
+
return
|
|
278
|
+
|
|
279
|
+
fig = plot_time_series_battery_soc(
|
|
280
|
+
battery_usecase_df,
|
|
281
|
+
time_col="timestamp",
|
|
282
|
+
title="",
|
|
283
|
+
xaxis_title="Zeitpunkt",
|
|
284
|
+
yaxis_title="kW",
|
|
285
|
+
battery_power_flow="battery_power_flow",
|
|
286
|
+
soc_pct="battery_soc_pct",
|
|
287
|
+
legend_title=None,
|
|
288
|
+
secondary_y_title="SOC [%]",
|
|
289
|
+
)
|
|
290
|
+
fig.update_layout(
|
|
291
|
+
height=_BATTERY_OVERVIEW_HEIGHT,
|
|
292
|
+
margin=_BATTERY_OVERVIEW_MARGIN,
|
|
293
|
+
xaxis_rangeslider_thickness=_BATTERY_OVERVIEW_RANGE_SLIDER_THICKNESS,
|
|
294
|
+
)
|
|
295
|
+
_left_align_plot_title(fig)
|
|
296
|
+
render_plot_card("Batterie Ladezustand", fig)
|
|
297
|
+
|
|
298
|
+
|
|
245
299
|
def _get_active_tabs(
|
|
246
|
-
tables: TablesResult,
|
|
300
|
+
tables: TablesResult,
|
|
301
|
+
mapper: ColumnMapper,
|
|
302
|
+
palette: dict[str, str],
|
|
303
|
+
component_types: Iterable[str],
|
|
247
304
|
) -> list[tuple[str, Callable[[], None]]]:
|
|
248
305
|
"""Determine which tabs should be rendered based on data availability."""
|
|
249
306
|
tabs = []
|
|
307
|
+
component_type_set = set(component_types)
|
|
250
308
|
|
|
251
309
|
# 1. Overview Tab
|
|
252
310
|
overview_df = _prepare_battery_usecase_df(tables)
|
|
253
311
|
if overview_df is not None and not overview_df.empty:
|
|
254
312
|
tabs.append(("Zeitreihen-Plot", lambda: _render_overview_plot(overview_df)))
|
|
313
|
+
if "battery" in component_type_set:
|
|
314
|
+
tabs.append(
|
|
315
|
+
(
|
|
316
|
+
"Batterie SOC",
|
|
317
|
+
lambda: _render_battery_soc_plot(overview_df),
|
|
318
|
+
)
|
|
319
|
+
)
|
|
255
320
|
|
|
256
321
|
# 2. Energy Mix Tab
|
|
257
322
|
power_table = tables.get("power_table")
|
|
@@ -293,6 +358,7 @@ def _get_active_tabs(
|
|
|
293
358
|
def render_plots_tabs(
|
|
294
359
|
tables: TablesResult,
|
|
295
360
|
mapper: ColumnMapper,
|
|
361
|
+
component_types: Iterable[str],
|
|
296
362
|
color_dict: dict[str, str] | None = None,
|
|
297
363
|
) -> None:
|
|
298
364
|
"""
|
|
@@ -301,12 +367,13 @@ def render_plots_tabs(
|
|
|
301
367
|
Args:
|
|
302
368
|
tables: The tables result containing data for the plots.
|
|
303
369
|
mapper: The column mapper for renaming columns.
|
|
370
|
+
component_types: Component type identifiers present in the microgrid.
|
|
304
371
|
color_dict: Optional color mapping for the plots.
|
|
305
372
|
"""
|
|
306
373
|
palette = color_dict or COLOR_DICT
|
|
307
374
|
|
|
308
375
|
# Get configuration of what to render
|
|
309
|
-
plot_tabs_config = _get_active_tabs(tables, mapper, palette)
|
|
376
|
+
plot_tabs_config = _get_active_tabs(tables, mapper, palette, component_types)
|
|
310
377
|
|
|
311
378
|
if not plot_tabs_config:
|
|
312
379
|
st.info("Keine Plot-Daten verfügbar.")
|
|
@@ -5,8 +5,6 @@
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
-
import json
|
|
9
|
-
|
|
10
8
|
import pandas as pd
|
|
11
9
|
import streamlit as st
|
|
12
10
|
from frequenz.lib.notebooks.reporting.utils.column_mapper import ColumnMapper
|
|
@@ -111,72 +109,6 @@ def _style_download_button(
|
|
|
111
109
|
)
|
|
112
110
|
|
|
113
111
|
|
|
114
|
-
def _render_reset_filters_button(key_prefix: str) -> None:
|
|
115
|
-
"""Render a no-rerun button that clears the matching AgGrid filters."""
|
|
116
|
-
button_id = f"{key_prefix}_reset_table_filters"
|
|
117
|
-
button_html = f"""
|
|
118
|
-
<button id="{button_id}" type="button">
|
|
119
|
-
Filter der Tabelle zurücksetzen
|
|
120
|
-
</button>
|
|
121
|
-
<script>
|
|
122
|
-
(function() {{
|
|
123
|
-
const button = document.getElementById({json.dumps(button_id)});
|
|
124
|
-
const gridKey = {json.dumps(key_prefix)};
|
|
125
|
-
const messageType = "cs-reporting-reset-aggrid-filters";
|
|
126
|
-
|
|
127
|
-
if (!button) {{
|
|
128
|
-
return;
|
|
129
|
-
}}
|
|
130
|
-
|
|
131
|
-
button.addEventListener("click", function(event) {{
|
|
132
|
-
event.preventDefault();
|
|
133
|
-
event.stopPropagation();
|
|
134
|
-
|
|
135
|
-
for (const iframe of window.parent.document.querySelectorAll("iframe")) {{
|
|
136
|
-
try {{
|
|
137
|
-
iframe.contentWindow.postMessage(
|
|
138
|
-
{{ type: messageType, gridKey: gridKey }},
|
|
139
|
-
"*"
|
|
140
|
-
);
|
|
141
|
-
}} catch (error) {{
|
|
142
|
-
// Ignore if a browser blocks access to an iframe window.
|
|
143
|
-
}}
|
|
144
|
-
}}
|
|
145
|
-
}});
|
|
146
|
-
}})();
|
|
147
|
-
</script>
|
|
148
|
-
<style>
|
|
149
|
-
html,
|
|
150
|
-
body {{
|
|
151
|
-
margin: 0;
|
|
152
|
-
padding: 0;
|
|
153
|
-
overflow: hidden;
|
|
154
|
-
background: transparent;
|
|
155
|
-
}}
|
|
156
|
-
#{button_id} {{
|
|
157
|
-
background-color: #fff4bf;
|
|
158
|
-
border: 1px solid #e4c34a;
|
|
159
|
-
color: #4f3f00;
|
|
160
|
-
border-radius: 6px;
|
|
161
|
-
cursor: pointer;
|
|
162
|
-
font-family: inherit;
|
|
163
|
-
font-size: 0.8rem;
|
|
164
|
-
font-weight: 600;
|
|
165
|
-
line-height: 1.1;
|
|
166
|
-
min-height: 1.75rem;
|
|
167
|
-
padding: 0.15rem 0.55rem;
|
|
168
|
-
white-space: nowrap;
|
|
169
|
-
}}
|
|
170
|
-
#{button_id}:hover {{
|
|
171
|
-
background-color: #ffe88a;
|
|
172
|
-
border-color: #c7a629;
|
|
173
|
-
color: #3f3200;
|
|
174
|
-
}}
|
|
175
|
-
</style>
|
|
176
|
-
"""
|
|
177
|
-
st.html(button_html, unsafe_allow_javascript=True)
|
|
178
|
-
|
|
179
|
-
|
|
180
112
|
def render_table_section(
|
|
181
113
|
df: pd.DataFrame | None,
|
|
182
114
|
*,
|
|
@@ -203,8 +135,6 @@ def render_table_section(
|
|
|
203
135
|
header_cols[0].caption(caption)
|
|
204
136
|
else:
|
|
205
137
|
header_cols[0].markdown("", unsafe_allow_html=True)
|
|
206
|
-
with header_cols[0]:
|
|
207
|
-
_render_reset_filters_button(key_prefix)
|
|
208
138
|
|
|
209
139
|
if not safe_df.empty:
|
|
210
140
|
csv_bytes = display_df.to_csv(index=False, sep=";", decimal=",").encode("utf-8")
|
|
@@ -236,8 +166,6 @@ def render_master_df(master_df: pd.DataFrame, mapper: ColumnMapper) -> None:
|
|
|
236
166
|
if master_df is not None and not master_df.empty:
|
|
237
167
|
header_cols = st.columns([1, 0.125])
|
|
238
168
|
header_cols[0].caption("Standardisierter Haupt-DataFrame")
|
|
239
|
-
with header_cols[0]:
|
|
240
|
-
_render_reset_filters_button("master_df")
|
|
241
169
|
display_df = _round_numeric_columns(
|
|
242
170
|
mapper.to_display(master_df).rename(columns=_MASTER_DF_DISPLAY_RENAMES)
|
|
243
171
|
)
|
|
@@ -1,93 +1,93 @@
|
|
|
1
1
|
{
|
|
2
2
|
"files": [
|
|
3
|
-
"mkdocs.yml",
|
|
4
|
-
"app.py",
|
|
5
|
-
"MANIFEST.in",
|
|
6
|
-
"CONTRIBUTING.md",
|
|
7
|
-
"Dockerfile",
|
|
8
|
-
".editorconfig",
|
|
9
|
-
"pyproject.toml",
|
|
10
3
|
".cookiecutter-replay.json",
|
|
11
|
-
"
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"README.md",
|
|
16
|
-
".gitignore",
|
|
4
|
+
".editorconfig",
|
|
5
|
+
".github/ISSUE_TEMPLATE/bug.yml",
|
|
6
|
+
".github/ISSUE_TEMPLATE/config.yml",
|
|
7
|
+
".github/ISSUE_TEMPLATE/feature.yml",
|
|
17
8
|
".github/RELEASE_NOTES.template.md",
|
|
18
|
-
".github/labeler.yml",
|
|
19
9
|
".github/dependabot.yml",
|
|
20
10
|
".github/keylabeler.yml",
|
|
21
|
-
".github/
|
|
22
|
-
".github/
|
|
23
|
-
".github/
|
|
24
|
-
".github/workflows/
|
|
11
|
+
".github/labeler.yml",
|
|
12
|
+
".github/workflows/auto-dependabot.yaml",
|
|
13
|
+
".github/workflows/black-migration.yaml",
|
|
14
|
+
".github/workflows/ci-pr.yaml",
|
|
25
15
|
".github/workflows/ci.yaml",
|
|
16
|
+
".github/workflows/dco-merge-queue.yml",
|
|
26
17
|
".github/workflows/isort-migration.yaml",
|
|
27
|
-
".github/workflows/release.yaml",
|
|
28
18
|
".github/workflows/labeler.yml",
|
|
29
|
-
".github/workflows/repo-config-migration.yaml",
|
|
30
|
-
".github/workflows/auto-dependabot.yaml",
|
|
31
|
-
".github/workflows/black-migration.yaml",
|
|
32
19
|
".github/workflows/release-notes-check.yml",
|
|
33
|
-
".github/workflows/
|
|
34
|
-
"
|
|
35
|
-
"
|
|
20
|
+
".github/workflows/release.yaml",
|
|
21
|
+
".github/workflows/repo-config-migration.yaml",
|
|
22
|
+
".gitignore",
|
|
36
23
|
".streamlit/config.toml",
|
|
37
|
-
"
|
|
24
|
+
"CODEOWNERS",
|
|
25
|
+
"CONTRIBUTING.md",
|
|
26
|
+
"Dockerfile",
|
|
27
|
+
"LICENSE",
|
|
28
|
+
"MANIFEST.in",
|
|
29
|
+
"README.md",
|
|
30
|
+
"RELEASE_NOTES.md",
|
|
31
|
+
"app.py",
|
|
32
|
+
"docs/CONTRIBUTING.md",
|
|
33
|
+
"docs/SUMMARY.md",
|
|
34
|
+
"docs/_css/mkdocstrings.css",
|
|
35
|
+
"docs/_css/style.css",
|
|
36
|
+
"docs/_img/logo.png",
|
|
37
|
+
"docs/_overrides/main.html",
|
|
38
|
+
"docs/_scripts/mkdocstrings_autoapi.py",
|
|
39
|
+
"docs/index.md",
|
|
40
|
+
"mkdocs.yml",
|
|
41
|
+
"noxfile.py",
|
|
42
|
+
"pyproject.toml",
|
|
38
43
|
"src/frequenz/cs_reporting/__init__.py",
|
|
39
|
-
"src/frequenz/cs_reporting/ui_resources.py",
|
|
40
|
-
"src/frequenz/cs_reporting/conftest.py",
|
|
41
|
-
"src/frequenz/cs_reporting/constants.py",
|
|
42
|
-
"src/frequenz/cs_reporting/utils/__init__.py",
|
|
43
|
-
"src/frequenz/cs_reporting/utils/env.py",
|
|
44
|
-
"src/frequenz/cs_reporting/utils/time.py",
|
|
45
44
|
"src/frequenz/cs_reporting/app_pages/__init__.py",
|
|
46
|
-
"src/frequenz/cs_reporting/app_pages/solar.py",
|
|
47
|
-
"src/frequenz/cs_reporting/app_pages/reporting.py",
|
|
48
45
|
"src/frequenz/cs_reporting/app_pages/home.py",
|
|
46
|
+
"src/frequenz/cs_reporting/app_pages/reporting.py",
|
|
47
|
+
"src/frequenz/cs_reporting/app_pages/solar.py",
|
|
48
|
+
"src/frequenz/cs_reporting/assets/__init__.py",
|
|
49
|
+
"src/frequenz/cs_reporting/assets/neustrom_background.png",
|
|
50
|
+
"src/frequenz/cs_reporting/assets/neustrom_logo.png",
|
|
51
|
+
"src/frequenz/cs_reporting/components/__init__.py",
|
|
52
|
+
"src/frequenz/cs_reporting/components/inputs.py",
|
|
53
|
+
"src/frequenz/cs_reporting/components/plot_charts.py",
|
|
54
|
+
"src/frequenz/cs_reporting/components/sidebar_inputs.py",
|
|
55
|
+
"src/frequenz/cs_reporting/components/tables.py",
|
|
56
|
+
"src/frequenz/cs_reporting/components/ui.py",
|
|
57
|
+
"src/frequenz/cs_reporting/conftest.py",
|
|
58
|
+
"src/frequenz/cs_reporting/constants.py",
|
|
59
|
+
"src/frequenz/cs_reporting/py.typed",
|
|
60
|
+
"src/frequenz/cs_reporting/rep_cs_core/__init__.py",
|
|
61
|
+
"src/frequenz/cs_reporting/rep_cs_core/config.py",
|
|
62
|
+
"src/frequenz/cs_reporting/rep_cs_core/page_spec.py",
|
|
49
63
|
"src/frequenz/cs_reporting/services/__init__.py",
|
|
50
64
|
"src/frequenz/cs_reporting/services/client_factory.py",
|
|
51
|
-
"src/frequenz/cs_reporting/services/solar_workflow.py",
|
|
52
65
|
"src/frequenz/cs_reporting/services/data_service.py",
|
|
53
|
-
"src/frequenz/cs_reporting/
|
|
54
|
-
"src/frequenz/cs_reporting/styles/home.css",
|
|
66
|
+
"src/frequenz/cs_reporting/services/solar_workflow.py",
|
|
55
67
|
"src/frequenz/cs_reporting/styles/dashboard.css",
|
|
68
|
+
"src/frequenz/cs_reporting/styles/home.css",
|
|
56
69
|
"src/frequenz/cs_reporting/styles/kpi.css",
|
|
57
|
-
"src/frequenz/cs_reporting/
|
|
58
|
-
"src/frequenz/cs_reporting/views/sections.py",
|
|
59
|
-
"src/frequenz/cs_reporting/views/plot_renderers.py",
|
|
60
|
-
"src/frequenz/cs_reporting/views/__init__.py",
|
|
61
|
-
"src/frequenz/cs_reporting/views/solar_results.py",
|
|
62
|
-
"src/frequenz/cs_reporting/views/dashboard.py",
|
|
63
|
-
"src/frequenz/cs_reporting/views/table_renderers.py",
|
|
64
|
-
"src/frequenz/cs_reporting/templates/home_stats.html",
|
|
65
|
-
"src/frequenz/cs_reporting/templates/home_hero.html",
|
|
66
|
-
"src/frequenz/cs_reporting/templates/home_feature_grid.html",
|
|
70
|
+
"src/frequenz/cs_reporting/styles/plot_card.css",
|
|
67
71
|
"src/frequenz/cs_reporting/templates/dashboard_section_divider.html",
|
|
72
|
+
"src/frequenz/cs_reporting/templates/home_feature_grid.html",
|
|
68
73
|
"src/frequenz/cs_reporting/templates/home_feature_item.html",
|
|
74
|
+
"src/frequenz/cs_reporting/templates/home_footer.html",
|
|
75
|
+
"src/frequenz/cs_reporting/templates/home_hero.html",
|
|
69
76
|
"src/frequenz/cs_reporting/templates/home_logo.html",
|
|
70
77
|
"src/frequenz/cs_reporting/templates/home_stat_item.html",
|
|
71
|
-
"src/frequenz/cs_reporting/templates/
|
|
72
|
-
"src/frequenz/cs_reporting/
|
|
73
|
-
"src/frequenz/cs_reporting/
|
|
74
|
-
"src/frequenz/cs_reporting/
|
|
75
|
-
"src/frequenz/cs_reporting/
|
|
76
|
-
"src/frequenz/cs_reporting/
|
|
77
|
-
"src/frequenz/cs_reporting/
|
|
78
|
-
"src/frequenz/cs_reporting/
|
|
79
|
-
"src/frequenz/cs_reporting/
|
|
80
|
-
"src/frequenz/cs_reporting/
|
|
81
|
-
"src/frequenz/cs_reporting/
|
|
82
|
-
"src/frequenz/cs_reporting/
|
|
83
|
-
"
|
|
84
|
-
"
|
|
85
|
-
"docs/SUMMARY.md",
|
|
86
|
-
"docs/index.md",
|
|
87
|
-
"docs/_css/mkdocstrings.css",
|
|
88
|
-
"docs/_css/style.css",
|
|
89
|
-
"docs/_img/logo.png",
|
|
90
|
-
"docs/_overrides/main.html",
|
|
91
|
-
"docs/_scripts/mkdocstrings_autoapi.py"
|
|
78
|
+
"src/frequenz/cs_reporting/templates/home_stats.html",
|
|
79
|
+
"src/frequenz/cs_reporting/ui_resources.py",
|
|
80
|
+
"src/frequenz/cs_reporting/utils/__init__.py",
|
|
81
|
+
"src/frequenz/cs_reporting/utils/env.py",
|
|
82
|
+
"src/frequenz/cs_reporting/utils/time.py",
|
|
83
|
+
"src/frequenz/cs_reporting/views/__init__.py",
|
|
84
|
+
"src/frequenz/cs_reporting/views/dashboard.py",
|
|
85
|
+
"src/frequenz/cs_reporting/views/metric_renderers.py",
|
|
86
|
+
"src/frequenz/cs_reporting/views/plot_renderers.py",
|
|
87
|
+
"src/frequenz/cs_reporting/views/sections.py",
|
|
88
|
+
"src/frequenz/cs_reporting/views/solar_results.py",
|
|
89
|
+
"src/frequenz/cs_reporting/views/table_renderers.py",
|
|
90
|
+
"tests/test_frequenz_cs_reporting.py",
|
|
91
|
+
"toml_directory/__init__.py"
|
|
92
92
|
]
|
|
93
93
|
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# Frequenz CS Reporting Library Release Notes
|
|
2
|
-
|
|
3
|
-
## Summary
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
## Upgrading
|
|
7
|
-
|
|
8
|
-
- Update dockerfile to have grpcio.
|
|
9
|
-
|
|
10
|
-
## New Features
|
|
11
|
-
|
|
12
|
-
- Added a "Filter der Tabelle zurücksetzen" control to reporting tables, allowing
|
|
13
|
-
users to clear AgGrid filters without rerunning the page.
|
|
14
|
-
|
|
15
|
-
## Bug Fixes
|
|
16
|
-
|
|
17
|
-
- Fixed reporting table pagination and sizing by using a stable page size and
|
|
18
|
-
calculated grid height instead of automatic page sizing.
|
|
19
|
-
- Improved reporting dashboard error handling for timeouts, missing microgrid
|
|
20
|
-
configuration, invalid filter parameters, service configuration problems, and
|
|
21
|
-
API/network failures.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/conftest.py
RENAMED
|
File without changes
|
{frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/constants.py
RENAMED
|
File without changes
|
{frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/styles/kpi.css
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/utils/env.py
RENAMED
|
File without changes
|
{frequenz_cs_reporting-0.4.4 → frequenz_cs_reporting-0.4.5}/src/frequenz/cs_reporting/utils/time.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|