frequenz-cs-reporting 0.4.2__tar.gz → 0.4.4__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.2 → frequenz_cs_reporting-0.4.4}/PKG-INFO +1 -1
- frequenz_cs_reporting-0.4.4/RELEASE_NOTES.md +21 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/app_pages/reporting.py +69 -11
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/components/tables.py +61 -4
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/views/solar_results.py +5 -1
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/views/table_renderers.py +85 -10
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz_cs_reporting.egg-info/PKG-INFO +1 -1
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz_cs_reporting.egg-info/scm_version.json +2 -2
- frequenz_cs_reporting-0.4.2/RELEASE_NOTES.md +0 -13
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/.streamlit/config.toml +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/Dockerfile +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/LICENSE +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/MANIFEST.in +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/README.md +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/app.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/pyproject.toml +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/setup.cfg +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/__init__.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/app_pages/__init__.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/app_pages/home.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/app_pages/solar.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/assets/__init__.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/assets/neustrom_background.png +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/assets/neustrom_logo.png +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/components/__init__.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/components/inputs.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/components/plot_charts.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/components/sidebar_inputs.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/components/ui.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/conftest.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/constants.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/py.typed +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/rep_cs_core/__init__.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/rep_cs_core/config.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/rep_cs_core/page_spec.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/services/__init__.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/services/client_factory.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/services/data_service.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/services/solar_workflow.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/styles/dashboard.css +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/styles/home.css +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/styles/kpi.css +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/styles/plot_card.css +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/templates/dashboard_section_divider.html +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/templates/home_feature_grid.html +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/templates/home_feature_item.html +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/templates/home_footer.html +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/templates/home_hero.html +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/templates/home_logo.html +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/templates/home_stat_item.html +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/templates/home_stats.html +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/ui_resources.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/utils/__init__.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/utils/env.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/utils/time.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/views/__init__.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/views/dashboard.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/views/metric_renderers.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/views/plot_renderers.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/views/sections.py +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz_cs_reporting.egg-info/SOURCES.txt +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz_cs_reporting.egg-info/dependency_links.txt +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz_cs_reporting.egg-info/requires.txt +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz_cs_reporting.egg-info/scm_file_list.json +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz_cs_reporting.egg-info/top_level.txt +0 -0
- {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/toml_directory/__init__.py +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
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.
|
|
@@ -10,7 +10,6 @@ from datetime import UTC, datetime, timedelta
|
|
|
10
10
|
|
|
11
11
|
import pandas as pd
|
|
12
12
|
import streamlit as st
|
|
13
|
-
import streamlit.components.v1 as components
|
|
14
13
|
from frequenz.lib.notebooks.reporting.utils.column_mapper import ColumnMapper
|
|
15
14
|
from frequenz.lib.notebooks.reporting.utils.helpers import (
|
|
16
15
|
normalize_date_for_reporting,
|
|
@@ -32,17 +31,16 @@ def _scroll_to_section_if_requested() -> None:
|
|
|
32
31
|
if st.query_params.get("section") != "data-export":
|
|
33
32
|
return
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
components.html(
|
|
34
|
+
st.html(
|
|
37
35
|
"""
|
|
38
36
|
<script>
|
|
39
|
-
const target =
|
|
37
|
+
const target = document.getElementById("data-export-section");
|
|
40
38
|
if (target) {
|
|
41
39
|
target.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
42
40
|
}
|
|
43
41
|
</script>
|
|
44
42
|
""",
|
|
45
|
-
|
|
43
|
+
unsafe_allow_javascript=True,
|
|
46
44
|
)
|
|
47
45
|
|
|
48
46
|
|
|
@@ -84,6 +82,68 @@ def _prepare_dataframe(df: pd.DataFrame) -> pd.DataFrame:
|
|
|
84
82
|
return df
|
|
85
83
|
|
|
86
84
|
|
|
85
|
+
def _format_data_loading_error(exc: Exception, microgrid_id: int) -> str:
|
|
86
|
+
"""Return a user-facing error message for a data loading failure."""
|
|
87
|
+
detail = str(exc).strip()
|
|
88
|
+
detail_suffix = f"\n\nDetails: {detail}" if detail else ""
|
|
89
|
+
|
|
90
|
+
if isinstance(exc, TimeoutError):
|
|
91
|
+
return (
|
|
92
|
+
"Zeitüberschreitung beim Laden der Microgrid-Daten. "
|
|
93
|
+
"Bitte wählen Sie einen anderen Zeitraum oder versuchen Sie es erneut."
|
|
94
|
+
f"{detail_suffix}"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
if isinstance(exc, KeyError):
|
|
98
|
+
return (
|
|
99
|
+
f"Microgrid {microgrid_id} wurde in den geladenen Konfigurationen "
|
|
100
|
+
f"nicht gefunden.{detail_suffix}"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
if isinstance(exc, ValueError):
|
|
104
|
+
return (
|
|
105
|
+
"Die ausgewählten Filterparameter sind ungültig. "
|
|
106
|
+
"Bitte prüfen Sie Zeitraum, Zeitzone und Auflösung."
|
|
107
|
+
f"{detail_suffix}"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
if isinstance(exc, RuntimeError):
|
|
111
|
+
if "environment variable" in detail:
|
|
112
|
+
return (
|
|
113
|
+
"Eine benötigte Umgebungsvariable fehlt. "
|
|
114
|
+
"Bitte prüfen Sie die API- und Service-Konfiguration."
|
|
115
|
+
f"{detail_suffix}"
|
|
116
|
+
)
|
|
117
|
+
if "config directory" in detail or "config files" in detail:
|
|
118
|
+
return (
|
|
119
|
+
"Microgrid-Konfigurationen konnten nicht geladen werden. "
|
|
120
|
+
"Bitte prüfen Sie das konfigurierte TOML-Verzeichnis."
|
|
121
|
+
f"{detail_suffix}"
|
|
122
|
+
)
|
|
123
|
+
if "active event loop" in detail:
|
|
124
|
+
return (
|
|
125
|
+
"Interner Laufzeitfehler beim Laden der Daten. "
|
|
126
|
+
"Der Datenabruf wurde aus einem ungültigen Kontext gestartet."
|
|
127
|
+
f"{detail_suffix}"
|
|
128
|
+
)
|
|
129
|
+
return (
|
|
130
|
+
"Microgrid-Daten konnten wegen eines Laufzeitfehlers nicht geladen werden."
|
|
131
|
+
f"{detail_suffix}"
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
if isinstance(exc, OSError):
|
|
135
|
+
return (
|
|
136
|
+
"Die Reporting-API konnte nicht erreicht werden. "
|
|
137
|
+
"Bitte prüfen Sie Netzwerkverbindung, API-URL und Zugangsdaten."
|
|
138
|
+
f"{detail_suffix}"
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
"Beim Laden der Microgrid-Daten ist ein unerwarteter Fehler aufgetreten."
|
|
143
|
+
f"{detail_suffix}"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
87
147
|
# pylint: disable=too-many-locals
|
|
88
148
|
def render() -> None:
|
|
89
149
|
"""Render the Frequenz Reporting Dashboard page.
|
|
@@ -131,16 +191,14 @@ def render() -> None:
|
|
|
131
191
|
st.error(str(exc))
|
|
132
192
|
st.stop()
|
|
133
193
|
|
|
134
|
-
# Fetch microgrid configuration
|
|
135
|
-
component_types = list(get_component_types(microgrid_id))
|
|
136
|
-
mcfg = get_microgrid_config(microgrid_id)
|
|
137
|
-
|
|
138
194
|
# We extend the fetch end date by 1 day to ensure the API returns data
|
|
139
195
|
# for the full duration of the selected end_time.
|
|
140
196
|
|
|
141
197
|
# Fetch data with error handling
|
|
142
198
|
try:
|
|
143
199
|
with st.spinner("Microgrid-Daten werden geladen..."):
|
|
200
|
+
component_types = list(get_component_types(microgrid_id))
|
|
201
|
+
mcfg = get_microgrid_config(microgrid_id)
|
|
144
202
|
df = get_microgrid_data(
|
|
145
203
|
microgrid_id=microgrid_id,
|
|
146
204
|
start_date=start_time,
|
|
@@ -148,8 +206,8 @@ def render() -> None:
|
|
|
148
206
|
end_date=end_time,
|
|
149
207
|
resolution=resolution,
|
|
150
208
|
)
|
|
151
|
-
except
|
|
152
|
-
st.error(
|
|
209
|
+
except Exception as exc: # pylint: disable=broad-except
|
|
210
|
+
st.error(_format_data_loading_error(exc, microgrid_id))
|
|
153
211
|
st.stop()
|
|
154
212
|
|
|
155
213
|
# Check for empty results
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
"""AgGrid helpers for rendering reporting tables."""
|
|
5
5
|
|
|
6
|
+
import json
|
|
7
|
+
|
|
6
8
|
import pandas as pd
|
|
7
9
|
import streamlit as st
|
|
8
10
|
from st_aggrid import ( # type: ignore[import-untyped]
|
|
@@ -10,17 +12,23 @@ from st_aggrid import ( # type: ignore[import-untyped]
|
|
|
10
12
|
ColumnsAutoSizeMode,
|
|
11
13
|
GridOptionsBuilder,
|
|
12
14
|
GridUpdateMode,
|
|
15
|
+
JsCode,
|
|
13
16
|
)
|
|
14
17
|
|
|
18
|
+
_GRID_HEADER_HEIGHT = 40
|
|
19
|
+
_GRID_ROW_HEIGHT = 44
|
|
20
|
+
_GRID_PAGINATION_HEIGHT = 56
|
|
21
|
+
_GRID_BORDER_HEIGHT = 2
|
|
22
|
+
|
|
15
23
|
|
|
16
24
|
# pylint: disable=too-many-arguments
|
|
17
25
|
def aggrid_table(
|
|
18
26
|
df: pd.DataFrame,
|
|
19
27
|
*,
|
|
20
28
|
key_prefix: str,
|
|
21
|
-
page_size: int =
|
|
29
|
+
page_size: int = 7,
|
|
22
30
|
header_color: str = "#1e4f87",
|
|
23
|
-
height: int =
|
|
31
|
+
height: int | None = None,
|
|
24
32
|
theme: str = "balham", # 'alpine' | 'balham' | 'material' | etc.
|
|
25
33
|
default_col_width: int = 180,
|
|
26
34
|
min_col_width: int = 160,
|
|
@@ -46,13 +54,27 @@ def aggrid_table(
|
|
|
46
54
|
# Initialize session state for page size if it doesn't exist
|
|
47
55
|
if f"{key_prefix}_page_size" not in st.session_state:
|
|
48
56
|
st.session_state[f"{key_prefix}_page_size"] = page_size
|
|
57
|
+
page_size = int(st.session_state[f"{key_prefix}_page_size"])
|
|
58
|
+
visible_rows = min(max(len(df), 1), page_size)
|
|
59
|
+
if height is None:
|
|
60
|
+
height = (
|
|
61
|
+
_GRID_HEADER_HEIGHT
|
|
62
|
+
+ (visible_rows * _GRID_ROW_HEIGHT)
|
|
63
|
+
+ _GRID_PAGINATION_HEIGHT
|
|
64
|
+
+ _GRID_BORDER_HEIGHT
|
|
65
|
+
)
|
|
49
66
|
|
|
50
67
|
# --- Build grid options from dataframe ---
|
|
51
68
|
gb = GridOptionsBuilder.from_dataframe(df)
|
|
52
69
|
gb.configure_pagination(
|
|
53
70
|
enabled=True,
|
|
54
|
-
paginationAutoPageSize=
|
|
55
|
-
|
|
71
|
+
paginationAutoPageSize=False,
|
|
72
|
+
paginationPageSize=page_size,
|
|
73
|
+
)
|
|
74
|
+
gb.configure_grid_options(
|
|
75
|
+
headerHeight=_GRID_HEADER_HEIGHT,
|
|
76
|
+
rowHeight=_GRID_ROW_HEIGHT,
|
|
77
|
+
paginationPageSizeSelector=False,
|
|
56
78
|
)
|
|
57
79
|
|
|
58
80
|
gb.configure_default_column(
|
|
@@ -70,6 +92,39 @@ def aggrid_table(
|
|
|
70
92
|
|
|
71
93
|
# Fit columns on first load
|
|
72
94
|
grid_options = gb.build()
|
|
95
|
+
grid_options["onGridReady"] = JsCode(f"""
|
|
96
|
+
function(params) {{
|
|
97
|
+
const gridKey = {json.dumps(key_prefix)};
|
|
98
|
+
const messageType = "cs-reporting-reset-aggrid-filters";
|
|
99
|
+
window.__csReportingAgGridResetHandlers =
|
|
100
|
+
window.__csReportingAgGridResetHandlers || {{}};
|
|
101
|
+
|
|
102
|
+
const previousHandler =
|
|
103
|
+
window.__csReportingAgGridResetHandlers[gridKey];
|
|
104
|
+
if (previousHandler) {{
|
|
105
|
+
window.removeEventListener("message", previousHandler);
|
|
106
|
+
}}
|
|
107
|
+
|
|
108
|
+
const resetFilters = function() {{
|
|
109
|
+
params.api.setFilterModel(null);
|
|
110
|
+
params.api.onFilterChanged();
|
|
111
|
+
params.api.paginationGoToFirstPage();
|
|
112
|
+
}};
|
|
113
|
+
|
|
114
|
+
const handler = function(event) {{
|
|
115
|
+
if (
|
|
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);
|
|
126
|
+
}}
|
|
127
|
+
""")
|
|
73
128
|
|
|
74
129
|
# --- Scoped CSS: restrained header + clean grid lines ---
|
|
75
130
|
container_id = f"agc_{key_prefix}"
|
|
@@ -107,7 +162,9 @@ def aggrid_table(
|
|
|
107
162
|
gridOptions=grid_options,
|
|
108
163
|
height=height,
|
|
109
164
|
theme=theme,
|
|
165
|
+
allow_unsafe_jscode=True,
|
|
110
166
|
update_mode=GridUpdateMode.NO_UPDATE,
|
|
167
|
+
update_on=[],
|
|
111
168
|
fit_columns_on_grid_load=False,
|
|
112
169
|
columns_auto_size_mode=ColumnsAutoSizeMode.NO_AUTOSIZE,
|
|
113
170
|
key=key_prefix,
|
|
@@ -9,6 +9,7 @@ import base64
|
|
|
9
9
|
import math
|
|
10
10
|
from io import BytesIO
|
|
11
11
|
from typing import Any
|
|
12
|
+
from urllib.parse import quote
|
|
12
13
|
|
|
13
14
|
import matplotlib.pyplot as plt
|
|
14
15
|
import streamlit as st
|
|
@@ -52,7 +53,10 @@ def _render_production_table(plot_data: SolarAnalysisData) -> None:
|
|
|
52
53
|
estimated_rows = max(1, math.ceil(len(table_view) / cards_per_row))
|
|
53
54
|
height = min(140 + estimated_rows * 430, 1600)
|
|
54
55
|
|
|
55
|
-
st.
|
|
56
|
+
st.iframe(
|
|
57
|
+
f"data:text/html;charset=utf-8,{quote(dashboard_html)}",
|
|
58
|
+
height=height,
|
|
59
|
+
)
|
|
56
60
|
|
|
57
61
|
|
|
58
62
|
def _ensure_card_styles() -> None:
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
|
+
import json
|
|
9
|
+
|
|
8
10
|
import pandas as pd
|
|
9
11
|
import streamlit as st
|
|
10
12
|
from frequenz.lib.notebooks.reporting.utils.column_mapper import ColumnMapper
|
|
@@ -109,6 +111,72 @@ def _style_download_button(
|
|
|
109
111
|
)
|
|
110
112
|
|
|
111
113
|
|
|
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
|
+
|
|
112
180
|
def render_table_section(
|
|
113
181
|
df: pd.DataFrame | None,
|
|
114
182
|
*,
|
|
@@ -130,11 +198,13 @@ def render_table_section(
|
|
|
130
198
|
"""
|
|
131
199
|
safe_df = df if (df is not None and not df.empty) else pd.DataFrame()
|
|
132
200
|
display_df = _round_numeric_columns(safe_df)
|
|
133
|
-
header_cols = st.columns([
|
|
201
|
+
header_cols = st.columns([1, 0.125])
|
|
134
202
|
if caption:
|
|
135
203
|
header_cols[0].caption(caption)
|
|
136
204
|
else:
|
|
137
205
|
header_cols[0].markdown("", unsafe_allow_html=True)
|
|
206
|
+
with header_cols[0]:
|
|
207
|
+
_render_reset_filters_button(key_prefix)
|
|
138
208
|
|
|
139
209
|
if not safe_df.empty:
|
|
140
210
|
csv_bytes = display_df.to_csv(index=False, sep=";", decimal=",").encode("utf-8")
|
|
@@ -164,8 +234,10 @@ def render_master_df(master_df: pd.DataFrame, mapper: ColumnMapper) -> None:
|
|
|
164
234
|
Streamlit components are rendered directly.
|
|
165
235
|
"""
|
|
166
236
|
if master_df is not None and not master_df.empty:
|
|
167
|
-
header_cols = st.columns([
|
|
168
|
-
header_cols[0].caption("
|
|
237
|
+
header_cols = st.columns([1, 0.125])
|
|
238
|
+
header_cols[0].caption("Standardisierter Haupt-DataFrame")
|
|
239
|
+
with header_cols[0]:
|
|
240
|
+
_render_reset_filters_button("master_df")
|
|
169
241
|
display_df = _round_numeric_columns(
|
|
170
242
|
mapper.to_display(master_df).rename(columns=_MASTER_DF_DISPLAY_RENAMES)
|
|
171
243
|
)
|
|
@@ -174,7 +246,7 @@ def render_master_df(master_df: pd.DataFrame, mapper: ColumnMapper) -> None:
|
|
|
174
246
|
)
|
|
175
247
|
_style_download_button(header_cols[1])
|
|
176
248
|
header_cols[1].download_button(
|
|
177
|
-
label="
|
|
249
|
+
label="CSV herunterladen",
|
|
178
250
|
data=master_csv,
|
|
179
251
|
file_name="master_df.csv",
|
|
180
252
|
mime="text/csv",
|
|
@@ -214,12 +286,19 @@ def render_data_tabs(
|
|
|
214
286
|
st.info("Keine Datentabellen verfügbar.")
|
|
215
287
|
return
|
|
216
288
|
|
|
217
|
-
tab_labels = [
|
|
289
|
+
tab_labels = []
|
|
218
290
|
if include_master:
|
|
219
291
|
tab_labels.append("Gesamt-Datensatz")
|
|
292
|
+
tab_labels.extend(spec["label"] for spec in available_specs)
|
|
220
293
|
tabs = st.tabs(tab_labels)
|
|
221
294
|
|
|
222
|
-
|
|
295
|
+
table_tabs = tabs
|
|
296
|
+
if include_master:
|
|
297
|
+
with tabs[0]:
|
|
298
|
+
render_master_df(master_df, mapper)
|
|
299
|
+
table_tabs = tabs[1:]
|
|
300
|
+
|
|
301
|
+
for tab, spec in zip(table_tabs, available_specs):
|
|
223
302
|
value = tables_dict.get(spec["table_key"])
|
|
224
303
|
with tab:
|
|
225
304
|
render_table_section(
|
|
@@ -228,7 +307,3 @@ def render_data_tabs(
|
|
|
228
307
|
caption=spec.get("caption"),
|
|
229
308
|
empty_info=spec.get("empty_info"),
|
|
230
309
|
)
|
|
231
|
-
|
|
232
|
-
if include_master:
|
|
233
|
-
with tabs[-1]:
|
|
234
|
-
render_master_df(master_df, mapper)
|
|
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.2 → frequenz_cs_reporting-0.4.4}/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.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/conftest.py
RENAMED
|
File without changes
|
{frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/constants.py
RENAMED
|
File without changes
|
{frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/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
|
|
File without changes
|
{frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/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.2 → frequenz_cs_reporting-0.4.4}/src/frequenz/cs_reporting/utils/env.py
RENAMED
|
File without changes
|
{frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.4}/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
|
|
File without changes
|
|
File without changes
|