frequenz-cs-reporting 0.4.2__tar.gz → 0.4.3__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.
Files changed (66) hide show
  1. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/PKG-INFO +1 -1
  2. frequenz_cs_reporting-0.4.3/RELEASE_NOTES.md +21 -0
  3. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/app_pages/reporting.py +69 -11
  4. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/components/tables.py +61 -4
  5. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/views/solar_results.py +5 -1
  6. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/views/table_renderers.py +85 -10
  7. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz_cs_reporting.egg-info/PKG-INFO +1 -1
  8. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz_cs_reporting.egg-info/scm_version.json +2 -2
  9. frequenz_cs_reporting-0.4.2/RELEASE_NOTES.md +0 -13
  10. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/.streamlit/config.toml +0 -0
  11. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/Dockerfile +0 -0
  12. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/LICENSE +0 -0
  13. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/MANIFEST.in +0 -0
  14. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/README.md +0 -0
  15. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/app.py +0 -0
  16. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/pyproject.toml +0 -0
  17. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/setup.cfg +0 -0
  18. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/__init__.py +0 -0
  19. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/app_pages/__init__.py +0 -0
  20. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/app_pages/home.py +0 -0
  21. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/app_pages/solar.py +0 -0
  22. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/assets/__init__.py +0 -0
  23. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/assets/neustrom_background.png +0 -0
  24. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/assets/neustrom_logo.png +0 -0
  25. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/components/__init__.py +0 -0
  26. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/components/inputs.py +0 -0
  27. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/components/plot_charts.py +0 -0
  28. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/components/sidebar_inputs.py +0 -0
  29. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/components/ui.py +0 -0
  30. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/conftest.py +0 -0
  31. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/constants.py +0 -0
  32. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/py.typed +0 -0
  33. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/rep_cs_core/__init__.py +0 -0
  34. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/rep_cs_core/config.py +0 -0
  35. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/rep_cs_core/page_spec.py +0 -0
  36. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/services/__init__.py +0 -0
  37. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/services/client_factory.py +0 -0
  38. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/services/data_service.py +0 -0
  39. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/services/solar_workflow.py +0 -0
  40. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/styles/dashboard.css +0 -0
  41. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/styles/home.css +0 -0
  42. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/styles/kpi.css +0 -0
  43. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/styles/plot_card.css +0 -0
  44. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/templates/dashboard_section_divider.html +0 -0
  45. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/templates/home_feature_grid.html +0 -0
  46. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/templates/home_feature_item.html +0 -0
  47. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/templates/home_footer.html +0 -0
  48. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/templates/home_hero.html +0 -0
  49. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/templates/home_logo.html +0 -0
  50. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/templates/home_stat_item.html +0 -0
  51. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/templates/home_stats.html +0 -0
  52. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/ui_resources.py +0 -0
  53. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/utils/__init__.py +0 -0
  54. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/utils/env.py +0 -0
  55. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/utils/time.py +0 -0
  56. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/views/__init__.py +0 -0
  57. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/views/dashboard.py +0 -0
  58. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/views/metric_renderers.py +0 -0
  59. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/views/plot_renderers.py +0 -0
  60. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz/cs_reporting/views/sections.py +0 -0
  61. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz_cs_reporting.egg-info/SOURCES.txt +0 -0
  62. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz_cs_reporting.egg-info/dependency_links.txt +0 -0
  63. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz_cs_reporting.egg-info/requires.txt +0 -0
  64. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz_cs_reporting.egg-info/scm_file_list.json +0 -0
  65. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/src/frequenz_cs_reporting.egg-info/top_level.txt +0 -0
  66. {frequenz_cs_reporting-0.4.2 → frequenz_cs_reporting-0.4.3}/toml_directory/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frequenz-cs-reporting
3
- Version: 0.4.2
3
+ Version: 0.4.3
4
4
  Summary: Streamlit Library for reporting and solar monitoring
5
5
  Author-email: Frequenz Energy-as-a-Service GmbH <floss@frequenz.com>
6
6
  License-Expression: MIT
@@ -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
- # Execute in parent document because Streamlit components render in an iframe.
36
- components.html(
34
+ st.html(
37
35
  """
38
36
  <script>
39
- const target = window.parent.document.getElementById("data-export-section");
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
- height=0,
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 (RuntimeError, ValueError, OSError) as e:
152
- st.error(f"Daten konnten nicht geladen werden: {e}")
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 = 12,
29
+ page_size: int = 7,
22
30
  header_color: str = "#1e4f87",
23
- height: int = 420,
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=True,
55
- # paginationPageSize=st.session_state[f'{key_prefix}_page_size'],
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.components.v1.html(dashboard_html, height=height, scrolling=True)
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([8, 1])
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([10, 1])
168
- header_cols[0].caption("Standardized master dataframe")
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="Download Gesamt-Datensatz",
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 = [spec["label"] for spec in available_specs]
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
- for tab, spec in zip(tabs, available_specs):
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frequenz-cs-reporting
3
- Version: 0.4.2
3
+ Version: 0.4.3
4
4
  Summary: Streamlit Library for reporting and solar monitoring
5
5
  Author-email: Frequenz Energy-as-a-Service GmbH <floss@frequenz.com>
6
6
  License-Expression: MIT
@@ -1,7 +1,7 @@
1
1
  {
2
- "tag": "0.4.2",
2
+ "tag": "0.4.3",
3
3
  "distance": 0,
4
- "node": "g961f18cd7b218235c1e9d85e021e877c3a3f1b04",
4
+ "node": "g547b1f4fb14278e0722c43562d10c16b33a6d896",
5
5
  "dirty": false,
6
6
  "branch": "HEAD",
7
7
  "node_date": "2026-08-04"
@@ -1,13 +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
-
13
- ## Bug Fixes