frequenz-cs-reporting 0.2.9__tar.gz → 0.4.0__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.2.9 → frequenz_cs_reporting-0.4.0}/PKG-INFO +14 -14
  2. frequenz_cs_reporting-0.4.0/RELEASE_NOTES.md +17 -0
  3. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/pyproject.toml +15 -15
  4. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/components/plot_charts.py +10 -3
  5. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/views/dashboard.py +1 -2
  6. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/views/metric_renderers.py +9 -2
  7. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/views/plot_renderers.py +21 -3
  8. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/views/solar_results.py +19 -25
  9. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/views/table_renderers.py +20 -6
  10. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz_cs_reporting.egg-info/PKG-INFO +14 -14
  11. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz_cs_reporting.egg-info/SOURCES.txt +2 -0
  12. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz_cs_reporting.egg-info/requires.txt +13 -13
  13. frequenz_cs_reporting-0.4.0/src/frequenz_cs_reporting.egg-info/scm_file_list.json +93 -0
  14. frequenz_cs_reporting-0.4.0/src/frequenz_cs_reporting.egg-info/scm_version.json +8 -0
  15. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/toml_directory/__init__.py +1 -0
  16. frequenz_cs_reporting-0.2.9/RELEASE_NOTES.md +0 -12
  17. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/.streamlit/config.toml +0 -0
  18. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/Dockerfile +0 -0
  19. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/LICENSE +0 -0
  20. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/MANIFEST.in +0 -0
  21. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/README.md +0 -0
  22. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/app.py +0 -0
  23. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/setup.cfg +0 -0
  24. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/__init__.py +0 -0
  25. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/app_pages/__init__.py +0 -0
  26. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/app_pages/home.py +0 -0
  27. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/app_pages/reporting.py +0 -0
  28. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/app_pages/solar.py +0 -0
  29. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/assets/__init__.py +0 -0
  30. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/assets/neustrom_background.png +0 -0
  31. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/assets/neustrom_logo.png +0 -0
  32. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/components/__init__.py +0 -0
  33. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/components/inputs.py +0 -0
  34. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/components/sidebar_inputs.py +0 -0
  35. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/components/tables.py +0 -0
  36. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/components/ui.py +0 -0
  37. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/conftest.py +0 -0
  38. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/constants.py +0 -0
  39. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/py.typed +0 -0
  40. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/rep_cs_core/__init__.py +0 -0
  41. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/rep_cs_core/config.py +0 -0
  42. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/rep_cs_core/page_spec.py +0 -0
  43. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/services/__init__.py +0 -0
  44. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/services/client_factory.py +0 -0
  45. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/services/data_service.py +0 -0
  46. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/services/solar_workflow.py +0 -0
  47. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/styles/dashboard.css +0 -0
  48. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/styles/home.css +0 -0
  49. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/styles/kpi.css +0 -0
  50. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/styles/plot_card.css +0 -0
  51. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/templates/dashboard_section_divider.html +0 -0
  52. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/templates/home_feature_grid.html +0 -0
  53. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/templates/home_feature_item.html +0 -0
  54. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/templates/home_footer.html +0 -0
  55. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/templates/home_hero.html +0 -0
  56. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/templates/home_logo.html +0 -0
  57. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/templates/home_stat_item.html +0 -0
  58. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/templates/home_stats.html +0 -0
  59. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/ui_resources.py +0 -0
  60. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/utils/__init__.py +0 -0
  61. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/utils/env.py +0 -0
  62. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/utils/time.py +0 -0
  63. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/views/__init__.py +0 -0
  64. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz/cs_reporting/views/sections.py +0 -0
  65. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz_cs_reporting.egg-info/dependency_links.txt +0 -0
  66. {frequenz_cs_reporting-0.2.9 → frequenz_cs_reporting-0.4.0}/src/frequenz_cs_reporting.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frequenz-cs-reporting
3
- Version: 0.2.9
3
+ Version: 0.4.0
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
@@ -20,7 +20,7 @@ Requires-Python: <4,>=3.11
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
22
  Requires-Dist: css-inline>=0.17.0
23
- Requires-Dist: frequenz-lib-notebooks<0.16.0,>=0.14.7
23
+ Requires-Dist: frequenz-lib-notebooks<0.17.0,>=0.16.0
24
24
  Requires-Dist: matplotlib>=3.10.6
25
25
  Requires-Dist: numpy>=2.3.2
26
26
  Requires-Dist: pandas>=2.3.2
@@ -38,39 +38,39 @@ Requires-Dist: flake8==7.3.0; extra == "dev-flake8"
38
38
  Requires-Dist: flake8-datetimez==20.10.0; extra == "dev-flake8"
39
39
  Requires-Dist: flake8-docstrings==1.7.0; extra == "dev-flake8"
40
40
  Requires-Dist: flake8-pyproject==1.2.4; extra == "dev-flake8"
41
- Requires-Dist: pydoclint==0.8.3; extra == "dev-flake8"
41
+ Requires-Dist: pydoclint==0.9.0; extra == "dev-flake8"
42
42
  Requires-Dist: pydocstyle==6.3.0; extra == "dev-flake8"
43
43
  Provides-Extra: dev-formatting
44
- Requires-Dist: black==26.3.1; extra == "dev-formatting"
44
+ Requires-Dist: black==26.5.1; extra == "dev-formatting"
45
45
  Requires-Dist: isort==8.0.1; extra == "dev-formatting"
46
46
  Provides-Extra: dev-mkdocs
47
47
  Requires-Dist: Markdown==3.10.2; extra == "dev-mkdocs"
48
- Requires-Dist: black==26.3.1; extra == "dev-mkdocs"
48
+ Requires-Dist: black==26.5.1; extra == "dev-mkdocs"
49
49
  Requires-Dist: mike==2.2.0; extra == "dev-mkdocs"
50
50
  Requires-Dist: mkdocs-gen-files==0.6.1; extra == "dev-mkdocs"
51
51
  Requires-Dist: mkdocs-literate-nav==0.6.3; extra == "dev-mkdocs"
52
52
  Requires-Dist: mkdocs-macros-plugin==1.5.0; extra == "dev-mkdocs"
53
53
  Requires-Dist: mkdocs-material==9.7.6; extra == "dev-mkdocs"
54
54
  Requires-Dist: mkdocstrings[python]==1.0.4; extra == "dev-mkdocs"
55
- Requires-Dist: mkdocstrings-python==2.0.3; extra == "dev-mkdocs"
56
- Requires-Dist: frequenz-repo-config[lib]==0.17.0; extra == "dev-mkdocs"
55
+ Requires-Dist: mkdocstrings-python==2.0.5; extra == "dev-mkdocs"
56
+ Requires-Dist: frequenz-repo-config[lib]==0.18.0; extra == "dev-mkdocs"
57
57
  Provides-Extra: dev-mypy
58
- Requires-Dist: mypy==1.20.1; extra == "dev-mypy"
59
- Requires-Dist: types-Markdown==3.10.2.20260408; extra == "dev-mypy"
58
+ Requires-Dist: mypy==2.1.0; extra == "dev-mypy"
59
+ Requires-Dist: types-Markdown==3.10.2.20260518; extra == "dev-mypy"
60
60
  Requires-Dist: types-pytz; extra == "dev-mypy"
61
61
  Requires-Dist: pandas-stubs; extra == "dev-mypy"
62
62
  Requires-Dist: frequenz-cs-reporting[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-mypy"
63
63
  Provides-Extra: dev-noxfile
64
64
  Requires-Dist: nox==2026.4.10; extra == "dev-noxfile"
65
- Requires-Dist: frequenz-repo-config[lib]==0.17.0; extra == "dev-noxfile"
65
+ Requires-Dist: frequenz-repo-config[lib]==0.18.0; extra == "dev-noxfile"
66
66
  Provides-Extra: dev-pylint
67
67
  Requires-Dist: frequenz-cs-reporting[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-pylint"
68
68
  Provides-Extra: dev-pytest
69
- Requires-Dist: pytest==9.0.3; extra == "dev-pytest"
70
- Requires-Dist: pylint==4.0.5; extra == "dev-pytest"
71
- Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.17.0; extra == "dev-pytest"
69
+ Requires-Dist: pytest==9.1.1; extra == "dev-pytest"
70
+ Requires-Dist: pylint==4.0.6; extra == "dev-pytest"
71
+ Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.18.0; extra == "dev-pytest"
72
72
  Requires-Dist: pytest-mock==3.15.1; extra == "dev-pytest"
73
- Requires-Dist: pytest-asyncio==1.3.0; extra == "dev-pytest"
73
+ Requires-Dist: pytest-asyncio==1.4.0; extra == "dev-pytest"
74
74
  Requires-Dist: async-solipsism==0.9; extra == "dev-pytest"
75
75
  Provides-Extra: dev
76
76
  Requires-Dist: frequenz-cs-reporting[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]; extra == "dev"
@@ -0,0 +1,17 @@
1
+ # Frequenz CS Reporting Library Release Notes
2
+
3
+ ## Summary
4
+
5
+ ## Upgrading
6
+
7
+ - Updating the minimum frequenz-lib-notebooks requirement to v0.16.0
8
+
9
+ ## New Features
10
+
11
+ - Update the radio button navigation for streamlit in deepnote.
12
+ - Add KWK aliases in the line plot.
13
+ - Update the KPIs to german format.
14
+ `- Replace the solar monitoring `Produktionsstatistiken` table with the
15
+ `MicrogridOverviewDashboard` view from `frequenz.lib.notebooks`.
16
+
17
+ ## Bug Fixes
@@ -4,8 +4,8 @@
4
4
  [build-system]
5
5
  requires = [
6
6
  "setuptools == 82.0.1",
7
- "setuptools_scm[toml] == 10.0.5",
8
- "frequenz-repo-config[lib] == 0.17.0",
7
+ "setuptools_scm[toml] == 10.2.0",
8
+ "frequenz-repo-config[lib] == 0.18.0",
9
9
  ]
10
10
  build-backend = "setuptools.build_meta"
11
11
 
@@ -29,7 +29,7 @@ requires-python = ">= 3.11, < 4"
29
29
 
30
30
  dependencies = [
31
31
  "css-inline>=0.17.0",
32
- "frequenz-lib-notebooks>=0.14.7,<0.16.0",
32
+ "frequenz-lib-notebooks>=0.16.0,<0.17.0",
33
33
  "matplotlib>=3.10.6",
34
34
  "numpy>=2.3.2",
35
35
  "pandas>=2.3.2",
@@ -54,25 +54,25 @@ dev-flake8 = [
54
54
  "flake8-datetimez == 20.10.0",
55
55
  "flake8-docstrings == 1.7.0",
56
56
  "flake8-pyproject == 1.2.4", # For reading the flake8 config from pyproject.toml
57
- "pydoclint == 0.8.3",
57
+ "pydoclint == 0.9.0",
58
58
  "pydocstyle == 6.3.0",
59
59
  ]
60
- dev-formatting = ["black == 26.3.1", "isort == 8.0.1"]
60
+ dev-formatting = ["black == 26.5.1", "isort == 8.0.1"]
61
61
  dev-mkdocs = [
62
62
  "Markdown == 3.10.2",
63
- "black == 26.3.1",
63
+ "black == 26.5.1",
64
64
  "mike == 2.2.0",
65
65
  "mkdocs-gen-files == 0.6.1",
66
66
  "mkdocs-literate-nav == 0.6.3",
67
67
  "mkdocs-macros-plugin == 1.5.0",
68
68
  "mkdocs-material == 9.7.6",
69
69
  "mkdocstrings[python] == 1.0.4",
70
- "mkdocstrings-python == 2.0.3",
71
- "frequenz-repo-config[lib] == 0.17.0",
70
+ "mkdocstrings-python == 2.0.5",
71
+ "frequenz-repo-config[lib] == 0.18.0",
72
72
  ]
73
73
  dev-mypy = [
74
- "mypy == 1.20.1",
75
- "types-Markdown == 3.10.2.20260408",
74
+ "mypy == 2.1.0",
75
+ "types-Markdown == 3.10.2.20260518",
76
76
  "types-pytz",
77
77
  "pandas-stubs",
78
78
  # For checking the noxfile, docs/ script, and tests
@@ -80,7 +80,7 @@ dev-mypy = [
80
80
  ]
81
81
  dev-noxfile = [
82
82
  "nox == 2026.4.10",
83
- "frequenz-repo-config[lib] == 0.17.0",
83
+ "frequenz-repo-config[lib] == 0.18.0",
84
84
  ]
85
85
  dev-pylint = [
86
86
  # dev-pytest already defines a dependency to pylint because of the examples
@@ -88,11 +88,11 @@ dev-pylint = [
88
88
  "frequenz-cs-reporting[dev-mkdocs,dev-noxfile,dev-pytest]",
89
89
  ]
90
90
  dev-pytest = [
91
- "pytest == 9.0.3",
92
- "pylint == 4.0.5", # We need this to check for the examples
93
- "frequenz-repo-config[extra-lint-examples] == 0.17.0",
91
+ "pytest == 9.1.1",
92
+ "pylint == 4.0.6", # We need this to check for the examples
93
+ "frequenz-repo-config[extra-lint-examples] == 0.18.0",
94
94
  "pytest-mock == 3.15.1",
95
- "pytest-asyncio == 1.3.0",
95
+ "pytest-asyncio == 1.4.0",
96
96
  "async-solipsism == 0.9",
97
97
  ]
98
98
  dev = [
@@ -135,6 +135,9 @@ def plot_percentage_bar(
135
135
 
136
136
  fig = go.Figure()
137
137
  for label, value, pct in seg_with_pct:
138
+ formatted_value = (
139
+ f"{value:,.0f}".replace(",", "X").replace(".", ",").replace("X", ".")
140
+ )
138
141
  fig.add_trace(
139
142
  go.Bar(
140
143
  x=[pct],
@@ -153,11 +156,11 @@ def plot_percentage_bar(
153
156
  "color": "#ffffff",
154
157
  "family": "Inter, sans-serif",
155
158
  },
156
- customdata=[value],
159
+ hovertext=[formatted_value],
157
160
  hovertemplate=(
158
161
  f"<b>{label}</b><br>"
159
162
  "Anteil: %{x:.1f}%<br>"
160
- "Energie: %{customdata[0]:,.0f} kWh"
163
+ "Energie: %{hovertext} kWh"
161
164
  "<extra></extra>"
162
165
  ),
163
166
  )
@@ -198,9 +201,13 @@ def plot_percentage_bar(
198
201
  "font_color": "#f8fafc",
199
202
  "bordercolor": "#334155",
200
203
  },
204
+ separators=",.",
201
205
  annotations=[
202
206
  {
203
- "text": f"<b>{total_key}:</b> {total:,.0f} kWh",
207
+ "text": f"<b>{total_key}:</b> {total:,.0f}".replace(",", "X")
208
+ .replace(".", ",")
209
+ .replace("X", ".")
210
+ + " kWh",
204
211
  "x": 0.5,
205
212
  "xref": "paper",
206
213
  "y": 1.35,
@@ -147,7 +147,6 @@ def build_master_df(
147
147
  for c in component_types
148
148
  if pd.to_numeric(raw_df[c], errors="coerce").fillna(0).sum() != 0
149
149
  ]
150
-
151
150
  master_df = create_energy_report_df(raw_df, component_types, mcfg, mapper)
152
151
  return master_df
153
152
 
@@ -193,4 +192,4 @@ def render_dashboard(
193
192
  # --- Tables section---
194
193
  st.markdown('<div id="data-export-section"></div>', unsafe_allow_html=True)
195
194
  _section_divider("Datentabellen")
196
- sections.render_data_tabs(master_df, tables)
195
+ sections.render_data_tabs(master_df, tables, mapper)
@@ -12,6 +12,13 @@ import streamlit as st
12
12
  from frequenz.cs_reporting.components.plot_charts import plot_percentage_bar
13
13
  from frequenz.cs_reporting.ui_resources import inject_style
14
14
 
15
+
16
+ def _fmt_de(val: float, decimals: int) -> str:
17
+ """Format a number in German locale (period thousands, comma decimal)."""
18
+ formatted = f"{val:,.{decimals}f}"
19
+ return formatted.replace(",", "X").replace(".", ",").replace("X", ".")
20
+
21
+
15
22
  # ── Section accent colours ─────────────────────────────────────────────────────
16
23
  # Each reporting section gets a distinct left-border colour so the dashboard
17
24
  # reads as a structured, visually hierarchical document.
@@ -154,9 +161,9 @@ def render_box_grid(
154
161
  '<div class="kpi-card__value kpi-card__value--null">—</div>'
155
162
  )
156
163
  elif isinstance(val, float) and val == int(val):
157
- value_html = f'<div class="kpi-card__value">{val:,.0f}</div>'
164
+ value_html = f'<div class="kpi-card__value">{_fmt_de(val, 0)}</div>'
158
165
  elif isinstance(val, (int, float)):
159
- value_html = f'<div class="kpi-card__value">{val:,.2f}</div>'
166
+ value_html = f'<div class="kpi-card__value">{_fmt_de(val, 2)}</div>'
160
167
  else:
161
168
  value_html = f'<div class="kpi-card__value">{val}</div>'
162
169
 
@@ -28,7 +28,7 @@ _DEFAULT_PLOT_ORDER = [
28
28
  "Zeitpunkt",
29
29
  "MID Gesamtverbrauch",
30
30
  "Netzbezug",
31
- "Netz Einspeisung",
31
+ "Netzeinspeisung",
32
32
  "PV-Erzeugung",
33
33
  "KWK-Erzeugung",
34
34
  "Wind-Erzeugung",
@@ -36,7 +36,18 @@ _DEFAULT_PLOT_ORDER = [
36
36
  ]
37
37
 
38
38
  _DEFAULT_DOTTED_COLS = ["Netzbezug"]
39
- _FILL_EXCLUDE = {"Zeitpunkt", "MID Gesamtverbrauch", "Netzbezug", "Netz Einspeisung"}
39
+ _FILL_EXCLUDE = {
40
+ "Zeitpunkt",
41
+ "MID Gesamtverbrauch",
42
+ "Netzbezug",
43
+ "Netzeinspeisung",
44
+ "Netz Einspeisung",
45
+ }
46
+ _OVERVIEW_ALIASES = {
47
+ "BHKW-Erzeugung": "KWK-Erzeugung",
48
+ "CHP": "KWK-Erzeugung",
49
+ "Netz Einspeisung": "Netzeinspeisung",
50
+ }
40
51
  _COMPONENT_TABS = [
41
52
  ("PV Leistung", "pv"),
42
53
  ("Batterie", "batt"),
@@ -214,7 +225,14 @@ def _prepare_overview_df(
214
225
 
215
226
  overview_df = mapper.to_display(overview_df)
216
227
  # Compatibility shim until the latest lib notebooks update rolls out
217
- return overview_df.rename(columns=COLUMN_RENAME_MAP)
228
+ overview_df = overview_df.rename(columns=COLUMN_RENAME_MAP)
229
+
230
+ # Normalize known legacy aliases so plot ordering always includes key series.
231
+ for source, target in _OVERVIEW_ALIASES.items():
232
+ if source in overview_df.columns and target not in overview_df.columns:
233
+ overview_df = overview_df.rename(columns={source: target})
234
+
235
+ return overview_df
218
236
 
219
237
 
220
238
  def _render_overview_plot(
@@ -6,12 +6,15 @@
6
6
  from __future__ import annotations
7
7
 
8
8
  import base64
9
+ import math
9
10
  from io import BytesIO
10
11
  from typing import Any
11
12
 
12
13
  import matplotlib.pyplot as plt
13
14
  import streamlit as st
14
- from frequenz.lib.notebooks.solar.maintenance.plot_styles import style_table
15
+ from frequenz.lib.notebooks.solar.maintenance.microgrid_dashboard import (
16
+ MicrogridOverviewDashboard,
17
+ )
15
18
  from frequenz.lib.notebooks.solar.maintenance.solar_maintenance_app import (
16
19
  SolarAnalysisData,
17
20
  )
@@ -19,10 +22,10 @@ from matplotlib.figure import Figure
19
22
 
20
23
 
21
24
  def _render_production_table(plot_data: SolarAnalysisData) -> None:
22
- """Render the production statistics table, if available.
25
+ """Render the production statistics dashboard, if available.
23
26
 
24
- Displays a styled, horizontally scrollable HTML table containing production
25
- statistics derived from the solar analysis data. If no production table is
27
+ Displays the production statistics using the card-based microgrid overview
28
+ dashboard provided by ``frequenz.lib.notebooks``. If no production table is
26
29
  present or it is empty, the function returns without rendering anything.
27
30
 
28
31
  Args:
@@ -30,35 +33,26 @@ def _render_production_table(plot_data: SolarAnalysisData) -> None:
30
33
  ``production_table_view`` attribute with tabular production statistics.
31
34
 
32
35
  Returns:
33
- None. The table is rendered directly in the Streamlit UI.
36
+ None. The dashboard is rendered directly in the Streamlit UI.
34
37
  """
35
38
  table_view = getattr(plot_data, "production_table_view", None)
36
39
  if table_view is None or table_view.empty:
37
40
  return
38
41
 
39
42
  st.markdown("#### Produktionsstatistiken")
40
- styled = style_table(table_view)
41
-
42
- # ---- Dynamic height calculation ----
43
- row_height = 55
44
- header_height = 75
45
- max_height = 600
46
-
47
- height = min(header_height + len(table_view) * row_height, max_height)
48
-
49
- scrollable_html = (
50
- "<style>"
51
- "table { min-width: 1200px; } "
52
- "thead th, tbody td { min-width: 150px; } "
53
- "thead th:first-child, tbody td:first-child { "
54
- "width: 60px; min-width: 60px; max-width: 80px; }"
55
- "</style>"
56
- "<div style='overflow-x: auto;'>"
57
- f"{styled.data}"
58
- "</div>"
43
+ dashboard = MicrogridOverviewDashboard(table_view, 1)
44
+ dashboard_html = dashboard.to_html().replace(
45
+ "justify-content: center;",
46
+ "justify-content: flex-start;",
59
47
  )
60
48
 
61
- st.components.v1.html(scrollable_html, height=height, scrolling=True)
49
+ # The notebook implementation uses IPython display(). In Streamlit we need
50
+ # the same generated HTML rendered through the component API instead.
51
+ cards_per_row = 2
52
+ estimated_rows = max(1, math.ceil(len(table_view) / cards_per_row))
53
+ height = min(140 + estimated_rows * 430, 1600)
54
+
55
+ st.components.v1.html(dashboard_html, height=height, scrolling=True)
62
56
 
63
57
 
64
58
  def _ensure_card_styles() -> None:
@@ -7,10 +7,16 @@ from __future__ import annotations
7
7
 
8
8
  import pandas as pd
9
9
  import streamlit as st
10
+ from frequenz.lib.notebooks.reporting.utils.column_mapper import ColumnMapper
10
11
 
11
12
  from frequenz.cs_reporting.components import tables
12
13
  from frequenz.cs_reporting.constants import TablesResult
13
14
 
15
+ _MASTER_DF_DISPLAY_RENAMES = {
16
+ "production_self_use": "Eigenverbrauch",
17
+ "production_self_usage": "Eigenverbrauch Share",
18
+ }
19
+
14
20
 
15
21
  def _round_numeric_columns(df: pd.DataFrame) -> pd.DataFrame:
16
22
  """Return a copy of `df` with number columns rounded to three decimals."""
@@ -131,7 +137,7 @@ def render_table_section(
131
137
  header_cols[0].markdown("", unsafe_allow_html=True)
132
138
 
133
139
  if not safe_df.empty:
134
- csv_bytes = display_df.to_csv(index=False).encode("utf-8")
140
+ csv_bytes = display_df.to_csv(index=False, sep=";", decimal=",").encode("utf-8")
135
141
  _style_download_button(header_cols[1])
136
142
  header_cols[1].download_button(
137
143
  label="CSV herunterladen",
@@ -147,11 +153,12 @@ def render_table_section(
147
153
  st.info(empty_info)
148
154
 
149
155
 
150
- def render_master_df(master_df: pd.DataFrame) -> None:
156
+ def render_master_df(master_df: pd.DataFrame, mapper: ColumnMapper) -> None:
151
157
  """Render the combined master dataframe in an AgGrid table.
152
158
 
153
159
  Args:
154
160
  master_df: Processed master dataframe to display.
161
+ mapper: Column mapper for converting canonical names to standard display labels.
155
162
 
156
163
  Returns:
157
164
  Streamlit components are rendered directly.
@@ -159,8 +166,12 @@ def render_master_df(master_df: pd.DataFrame) -> None:
159
166
  if master_df is not None and not master_df.empty:
160
167
  header_cols = st.columns([10, 1])
161
168
  header_cols[0].caption("Standardized master dataframe")
162
- display_df = _round_numeric_columns(master_df)
163
- master_csv = display_df.to_csv(index=False).encode("utf-8")
169
+ display_df = _round_numeric_columns(
170
+ mapper.to_display(master_df).rename(columns=_MASTER_DF_DISPLAY_RENAMES)
171
+ )
172
+ master_csv = display_df.to_csv(index=False, sep=";", decimal=",").encode(
173
+ "utf-8"
174
+ )
164
175
  _style_download_button(header_cols[1])
165
176
  header_cols[1].download_button(
166
177
  label="Download Gesamt-Datensatz",
@@ -177,12 +188,15 @@ def render_master_df(master_df: pd.DataFrame) -> None:
177
188
  st.info("Master-DF nicht verfügbar (keine MicrogridConfig).")
178
189
 
179
190
 
180
- def render_data_tabs(master_df: pd.DataFrame, tables_dict: TablesResult) -> None:
191
+ def render_data_tabs(
192
+ master_df: pd.DataFrame, tables_dict: TablesResult, mapper: ColumnMapper
193
+ ) -> None:
181
194
  """Render tabbed data tables for overview and component analyses.
182
195
 
183
196
  Args:
184
197
  master_df: Processed master dataframe.
185
198
  tables_dict: Mapping of table names to dataframes for each analysis.
199
+ mapper: Column mapper for standardizing Gesamt-Datensatz column names.
186
200
 
187
201
  Returns:
188
202
  Streamlit components are rendered directly.
@@ -217,4 +231,4 @@ def render_data_tabs(master_df: pd.DataFrame, tables_dict: TablesResult) -> None
217
231
 
218
232
  if include_master:
219
233
  with tabs[-1]:
220
- render_master_df(master_df)
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.2.9
3
+ Version: 0.4.0
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
@@ -20,7 +20,7 @@ Requires-Python: <4,>=3.11
20
20
  Description-Content-Type: text/markdown
21
21
  License-File: LICENSE
22
22
  Requires-Dist: css-inline>=0.17.0
23
- Requires-Dist: frequenz-lib-notebooks<0.16.0,>=0.14.7
23
+ Requires-Dist: frequenz-lib-notebooks<0.17.0,>=0.16.0
24
24
  Requires-Dist: matplotlib>=3.10.6
25
25
  Requires-Dist: numpy>=2.3.2
26
26
  Requires-Dist: pandas>=2.3.2
@@ -38,39 +38,39 @@ Requires-Dist: flake8==7.3.0; extra == "dev-flake8"
38
38
  Requires-Dist: flake8-datetimez==20.10.0; extra == "dev-flake8"
39
39
  Requires-Dist: flake8-docstrings==1.7.0; extra == "dev-flake8"
40
40
  Requires-Dist: flake8-pyproject==1.2.4; extra == "dev-flake8"
41
- Requires-Dist: pydoclint==0.8.3; extra == "dev-flake8"
41
+ Requires-Dist: pydoclint==0.9.0; extra == "dev-flake8"
42
42
  Requires-Dist: pydocstyle==6.3.0; extra == "dev-flake8"
43
43
  Provides-Extra: dev-formatting
44
- Requires-Dist: black==26.3.1; extra == "dev-formatting"
44
+ Requires-Dist: black==26.5.1; extra == "dev-formatting"
45
45
  Requires-Dist: isort==8.0.1; extra == "dev-formatting"
46
46
  Provides-Extra: dev-mkdocs
47
47
  Requires-Dist: Markdown==3.10.2; extra == "dev-mkdocs"
48
- Requires-Dist: black==26.3.1; extra == "dev-mkdocs"
48
+ Requires-Dist: black==26.5.1; extra == "dev-mkdocs"
49
49
  Requires-Dist: mike==2.2.0; extra == "dev-mkdocs"
50
50
  Requires-Dist: mkdocs-gen-files==0.6.1; extra == "dev-mkdocs"
51
51
  Requires-Dist: mkdocs-literate-nav==0.6.3; extra == "dev-mkdocs"
52
52
  Requires-Dist: mkdocs-macros-plugin==1.5.0; extra == "dev-mkdocs"
53
53
  Requires-Dist: mkdocs-material==9.7.6; extra == "dev-mkdocs"
54
54
  Requires-Dist: mkdocstrings[python]==1.0.4; extra == "dev-mkdocs"
55
- Requires-Dist: mkdocstrings-python==2.0.3; extra == "dev-mkdocs"
56
- Requires-Dist: frequenz-repo-config[lib]==0.17.0; extra == "dev-mkdocs"
55
+ Requires-Dist: mkdocstrings-python==2.0.5; extra == "dev-mkdocs"
56
+ Requires-Dist: frequenz-repo-config[lib]==0.18.0; extra == "dev-mkdocs"
57
57
  Provides-Extra: dev-mypy
58
- Requires-Dist: mypy==1.20.1; extra == "dev-mypy"
59
- Requires-Dist: types-Markdown==3.10.2.20260408; extra == "dev-mypy"
58
+ Requires-Dist: mypy==2.1.0; extra == "dev-mypy"
59
+ Requires-Dist: types-Markdown==3.10.2.20260518; extra == "dev-mypy"
60
60
  Requires-Dist: types-pytz; extra == "dev-mypy"
61
61
  Requires-Dist: pandas-stubs; extra == "dev-mypy"
62
62
  Requires-Dist: frequenz-cs-reporting[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-mypy"
63
63
  Provides-Extra: dev-noxfile
64
64
  Requires-Dist: nox==2026.4.10; extra == "dev-noxfile"
65
- Requires-Dist: frequenz-repo-config[lib]==0.17.0; extra == "dev-noxfile"
65
+ Requires-Dist: frequenz-repo-config[lib]==0.18.0; extra == "dev-noxfile"
66
66
  Provides-Extra: dev-pylint
67
67
  Requires-Dist: frequenz-cs-reporting[dev-mkdocs,dev-noxfile,dev-pytest]; extra == "dev-pylint"
68
68
  Provides-Extra: dev-pytest
69
- Requires-Dist: pytest==9.0.3; extra == "dev-pytest"
70
- Requires-Dist: pylint==4.0.5; extra == "dev-pytest"
71
- Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.17.0; extra == "dev-pytest"
69
+ Requires-Dist: pytest==9.1.1; extra == "dev-pytest"
70
+ Requires-Dist: pylint==4.0.6; extra == "dev-pytest"
71
+ Requires-Dist: frequenz-repo-config[extra-lint-examples]==0.18.0; extra == "dev-pytest"
72
72
  Requires-Dist: pytest-mock==3.15.1; extra == "dev-pytest"
73
- Requires-Dist: pytest-asyncio==1.3.0; extra == "dev-pytest"
73
+ Requires-Dist: pytest-asyncio==1.4.0; extra == "dev-pytest"
74
74
  Requires-Dist: async-solipsism==0.9; extra == "dev-pytest"
75
75
  Provides-Extra: dev
76
76
  Requires-Dist: frequenz-cs-reporting[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,dev-pylint,dev-pytest]; extra == "dev"
@@ -57,5 +57,7 @@ src/frequenz_cs_reporting.egg-info/PKG-INFO
57
57
  src/frequenz_cs_reporting.egg-info/SOURCES.txt
58
58
  src/frequenz_cs_reporting.egg-info/dependency_links.txt
59
59
  src/frequenz_cs_reporting.egg-info/requires.txt
60
+ src/frequenz_cs_reporting.egg-info/scm_file_list.json
61
+ src/frequenz_cs_reporting.egg-info/scm_version.json
60
62
  src/frequenz_cs_reporting.egg-info/top_level.txt
61
63
  toml_directory/__init__.py
@@ -1,5 +1,5 @@
1
1
  css-inline>=0.17.0
2
- frequenz-lib-notebooks<0.16.0,>=0.14.7
2
+ frequenz-lib-notebooks<0.17.0,>=0.16.0
3
3
  matplotlib>=3.10.6
4
4
  numpy>=2.3.2
5
5
  pandas>=2.3.2
@@ -21,43 +21,43 @@ flake8==7.3.0
21
21
  flake8-datetimez==20.10.0
22
22
  flake8-docstrings==1.7.0
23
23
  flake8-pyproject==1.2.4
24
- pydoclint==0.8.3
24
+ pydoclint==0.9.0
25
25
  pydocstyle==6.3.0
26
26
 
27
27
  [dev-formatting]
28
- black==26.3.1
28
+ black==26.5.1
29
29
  isort==8.0.1
30
30
 
31
31
  [dev-mkdocs]
32
32
  Markdown==3.10.2
33
- black==26.3.1
33
+ black==26.5.1
34
34
  mike==2.2.0
35
35
  mkdocs-gen-files==0.6.1
36
36
  mkdocs-literate-nav==0.6.3
37
37
  mkdocs-macros-plugin==1.5.0
38
38
  mkdocs-material==9.7.6
39
39
  mkdocstrings[python]==1.0.4
40
- mkdocstrings-python==2.0.3
41
- frequenz-repo-config[lib]==0.17.0
40
+ mkdocstrings-python==2.0.5
41
+ frequenz-repo-config[lib]==0.18.0
42
42
 
43
43
  [dev-mypy]
44
- mypy==1.20.1
45
- types-Markdown==3.10.2.20260408
44
+ mypy==2.1.0
45
+ types-Markdown==3.10.2.20260518
46
46
  types-pytz
47
47
  pandas-stubs
48
48
  frequenz-cs-reporting[dev-mkdocs,dev-noxfile,dev-pytest]
49
49
 
50
50
  [dev-noxfile]
51
51
  nox==2026.4.10
52
- frequenz-repo-config[lib]==0.17.0
52
+ frequenz-repo-config[lib]==0.18.0
53
53
 
54
54
  [dev-pylint]
55
55
  frequenz-cs-reporting[dev-mkdocs,dev-noxfile,dev-pytest]
56
56
 
57
57
  [dev-pytest]
58
- pytest==9.0.3
59
- pylint==4.0.5
60
- frequenz-repo-config[extra-lint-examples]==0.17.0
58
+ pytest==9.1.1
59
+ pylint==4.0.6
60
+ frequenz-repo-config[extra-lint-examples]==0.18.0
61
61
  pytest-mock==3.15.1
62
- pytest-asyncio==1.3.0
62
+ pytest-asyncio==1.4.0
63
63
  async-solipsism==0.9
@@ -0,0 +1,93 @@
1
+ {
2
+ "files": [
3
+ ".gitignore",
4
+ "README.md",
5
+ "CONTRIBUTING.md",
6
+ ".editorconfig",
7
+ ".cookiecutter-replay.json",
8
+ "Dockerfile",
9
+ "pyproject.toml",
10
+ "MANIFEST.in",
11
+ "app.py",
12
+ "RELEASE_NOTES.md",
13
+ "LICENSE",
14
+ "mkdocs.yml",
15
+ "CODEOWNERS",
16
+ "noxfile.py",
17
+ "toml_directory/__init__.py",
18
+ "src/frequenz/cs_reporting/conftest.py",
19
+ "src/frequenz/cs_reporting/ui_resources.py",
20
+ "src/frequenz/cs_reporting/constants.py",
21
+ "src/frequenz/cs_reporting/py.typed",
22
+ "src/frequenz/cs_reporting/__init__.py",
23
+ "src/frequenz/cs_reporting/services/solar_workflow.py",
24
+ "src/frequenz/cs_reporting/services/client_factory.py",
25
+ "src/frequenz/cs_reporting/services/data_service.py",
26
+ "src/frequenz/cs_reporting/services/__init__.py",
27
+ "src/frequenz/cs_reporting/utils/env.py",
28
+ "src/frequenz/cs_reporting/utils/time.py",
29
+ "src/frequenz/cs_reporting/utils/__init__.py",
30
+ "src/frequenz/cs_reporting/templates/home_footer.html",
31
+ "src/frequenz/cs_reporting/templates/home_feature_grid.html",
32
+ "src/frequenz/cs_reporting/templates/home_feature_item.html",
33
+ "src/frequenz/cs_reporting/templates/home_hero.html",
34
+ "src/frequenz/cs_reporting/templates/home_stats.html",
35
+ "src/frequenz/cs_reporting/templates/home_logo.html",
36
+ "src/frequenz/cs_reporting/templates/home_stat_item.html",
37
+ "src/frequenz/cs_reporting/templates/dashboard_section_divider.html",
38
+ "src/frequenz/cs_reporting/styles/dashboard.css",
39
+ "src/frequenz/cs_reporting/styles/home.css",
40
+ "src/frequenz/cs_reporting/styles/plot_card.css",
41
+ "src/frequenz/cs_reporting/styles/kpi.css",
42
+ "src/frequenz/cs_reporting/components/sidebar_inputs.py",
43
+ "src/frequenz/cs_reporting/components/tables.py",
44
+ "src/frequenz/cs_reporting/components/ui.py",
45
+ "src/frequenz/cs_reporting/components/inputs.py",
46
+ "src/frequenz/cs_reporting/components/plot_charts.py",
47
+ "src/frequenz/cs_reporting/components/__init__.py",
48
+ "src/frequenz/cs_reporting/app_pages/home.py",
49
+ "src/frequenz/cs_reporting/app_pages/reporting.py",
50
+ "src/frequenz/cs_reporting/app_pages/solar.py",
51
+ "src/frequenz/cs_reporting/app_pages/__init__.py",
52
+ "src/frequenz/cs_reporting/assets/neustrom_logo.png",
53
+ "src/frequenz/cs_reporting/assets/neustrom_background.png",
54
+ "src/frequenz/cs_reporting/assets/__init__.py",
55
+ "src/frequenz/cs_reporting/rep_cs_core/config.py",
56
+ "src/frequenz/cs_reporting/rep_cs_core/page_spec.py",
57
+ "src/frequenz/cs_reporting/rep_cs_core/__init__.py",
58
+ "src/frequenz/cs_reporting/views/plot_renderers.py",
59
+ "src/frequenz/cs_reporting/views/metric_renderers.py",
60
+ "src/frequenz/cs_reporting/views/table_renderers.py",
61
+ "src/frequenz/cs_reporting/views/sections.py",
62
+ "src/frequenz/cs_reporting/views/solar_results.py",
63
+ "src/frequenz/cs_reporting/views/dashboard.py",
64
+ "src/frequenz/cs_reporting/views/__init__.py",
65
+ ".github/labeler.yml",
66
+ ".github/dependabot.yml",
67
+ ".github/keylabeler.yml",
68
+ ".github/RELEASE_NOTES.template.md",
69
+ ".github/workflows/isort-migration.yaml",
70
+ ".github/workflows/labeler.yml",
71
+ ".github/workflows/repo-config-migration.yaml",
72
+ ".github/workflows/ci-pr.yaml",
73
+ ".github/workflows/ci.yaml",
74
+ ".github/workflows/black-migration.yaml",
75
+ ".github/workflows/dco-merge-queue.yml",
76
+ ".github/workflows/release-notes-check.yml",
77
+ ".github/workflows/auto-dependabot.yaml",
78
+ ".github/workflows/release.yaml",
79
+ ".github/ISSUE_TEMPLATE/config.yml",
80
+ ".github/ISSUE_TEMPLATE/feature.yml",
81
+ ".github/ISSUE_TEMPLATE/bug.yml",
82
+ ".streamlit/config.toml",
83
+ "tests/test_frequenz_cs_reporting.py",
84
+ "docs/SUMMARY.md",
85
+ "docs/CONTRIBUTING.md",
86
+ "docs/index.md",
87
+ "docs/_img/logo.png",
88
+ "docs/_css/mkdocstrings.css",
89
+ "docs/_css/style.css",
90
+ "docs/_scripts/mkdocstrings_autoapi.py",
91
+ "docs/_overrides/main.html"
92
+ ]
93
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "0.4.0",
3
+ "distance": 0,
4
+ "node": "gb14bb5bb0041b0254d8584079fc930047d8d1fca",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-07-17"
8
+ }
@@ -2,4 +2,5 @@
2
2
  # Copyright © 2026 Frequenz Energy-as-a-Service GmbH
3
3
 
4
4
  """Directory for TOML configuration files."""
5
+
5
6
  from __future__ import annotations
@@ -1,12 +0,0 @@
1
- # Frequenz CS Reporting Library Release Notes
2
-
3
- ## Summary
4
-
5
- ## Upgrading
6
-
7
-
8
- ## New Features
9
-
10
- - Update the radio button navigation for streamlit in deepnote.
11
-
12
- ## Bug Fixes