frequenz-cs-reporting 0.2.8__tar.gz → 0.2.10__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 (64) hide show
  1. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/PKG-INFO +15 -15
  2. frequenz_cs_reporting-0.2.10/RELEASE_NOTES.md +14 -0
  3. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/app.py +14 -3
  4. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/pyproject.toml +16 -15
  5. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/app_pages/home.py +11 -5
  6. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/app_pages/reporting.py +3 -3
  7. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/app_pages/solar.py +3 -1
  8. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/components/plot_charts.py +5 -1
  9. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/components/sidebar_inputs.py +2 -2
  10. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/services/solar_workflow.py +3 -3
  11. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/utils/time.py +17 -4
  12. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/views/metric_renderers.py +9 -2
  13. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/views/plot_renderers.py +21 -3
  14. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/views/table_renderers.py +4 -2
  15. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz_cs_reporting.egg-info/PKG-INFO +15 -15
  16. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz_cs_reporting.egg-info/requires.txt +13 -12
  17. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/toml_directory/__init__.py +1 -0
  18. frequenz_cs_reporting-0.2.8/RELEASE_NOTES.md +0 -12
  19. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/.streamlit/config.toml +0 -0
  20. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/Dockerfile +0 -0
  21. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/LICENSE +0 -0
  22. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/MANIFEST.in +0 -0
  23. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/README.md +0 -0
  24. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/setup.cfg +0 -0
  25. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/__init__.py +0 -0
  26. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/app_pages/__init__.py +0 -0
  27. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/assets/__init__.py +0 -0
  28. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/assets/neustrom_background.png +0 -0
  29. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/assets/neustrom_logo.png +0 -0
  30. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/components/__init__.py +0 -0
  31. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/components/inputs.py +0 -0
  32. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/components/tables.py +0 -0
  33. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/components/ui.py +0 -0
  34. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/conftest.py +0 -0
  35. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/constants.py +0 -0
  36. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/py.typed +0 -0
  37. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/rep_cs_core/__init__.py +0 -0
  38. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/rep_cs_core/config.py +0 -0
  39. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/rep_cs_core/page_spec.py +0 -0
  40. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/services/__init__.py +0 -0
  41. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/services/client_factory.py +0 -0
  42. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/services/data_service.py +0 -0
  43. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/styles/dashboard.css +0 -0
  44. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/styles/home.css +0 -0
  45. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/styles/kpi.css +0 -0
  46. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/styles/plot_card.css +0 -0
  47. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/templates/dashboard_section_divider.html +0 -0
  48. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/templates/home_feature_grid.html +0 -0
  49. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/templates/home_feature_item.html +0 -0
  50. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/templates/home_footer.html +0 -0
  51. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/templates/home_hero.html +0 -0
  52. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/templates/home_logo.html +0 -0
  53. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/templates/home_stat_item.html +0 -0
  54. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/templates/home_stats.html +0 -0
  55. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/ui_resources.py +0 -0
  56. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/utils/__init__.py +0 -0
  57. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/utils/env.py +0 -0
  58. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/views/__init__.py +0 -0
  59. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/views/dashboard.py +0 -0
  60. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/views/sections.py +0 -0
  61. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz/cs_reporting/views/solar_results.py +0 -0
  62. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz_cs_reporting.egg-info/SOURCES.txt +0 -0
  63. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz_cs_reporting.egg-info/dependency_links.txt +0 -0
  64. {frequenz_cs_reporting-0.2.8 → frequenz_cs_reporting-0.2.10}/src/frequenz_cs_reporting.egg-info/top_level.txt +0 -0
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frequenz-cs-reporting
3
- Version: 0.2.8
3
+ Version: 0.2.10
4
4
  Summary: Streamlit Library for reporting and solar monitoring
5
5
  Author-email: Frequenz Energy-as-a-Service GmbH <floss@frequenz.com>
6
- License: MIT
6
+ License-Expression: MIT
7
7
  Project-URL: Documentation, https://frequenz-floss.github.io/frequenz-cs-reporting/
8
8
  Project-URL: Changelog, https://github.com/frequenz-floss/frequenz-cs-reporting/releases
9
9
  Project-URL: Issues, https://github.com/frequenz-floss/frequenz-cs-reporting/issues
@@ -12,7 +12,6 @@ Project-URL: Support, https://github.com/frequenz-floss/frequenz-cs-reporting/di
12
12
  Keywords: frequenz,python,lib,library,frequenz-cs-reporting,tooling,notebooks,streamlit
13
13
  Classifier: Development Status :: 3 - Alpha
14
14
  Classifier: Intended Audience :: Developers
15
- Classifier: License :: OSI Approved :: MIT License
16
15
  Classifier: Programming Language :: Python :: 3
17
16
  Classifier: Programming Language :: Python :: 3 :: Only
18
17
  Classifier: Topic :: Software Development :: Libraries
@@ -36,41 +35,42 @@ Requires-Dist: streamlit-aggrid>=1.1.8.post1
36
35
  Requires-Dist: watchdog>=6.0.0
37
36
  Provides-Extra: dev-flake8
38
37
  Requires-Dist: flake8==7.3.0; extra == "dev-flake8"
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.8.6; 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.14.0; extra == "dev-mkdocs"
55
+ Requires-Dist: mkdocstrings-python==2.0.4; 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.20260211; 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.14.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.14.0; extra == "dev-pytest"
69
+ Requires-Dist: pytest==9.1.0; 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,14 @@
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
+ - Add KWK aliases in the line plot.
12
+ - Update the KPIs to german format.
13
+
14
+ ## Bug Fixes
@@ -261,8 +261,12 @@ def sidebar(pages: list[PageSpec]) -> PageSpec:
261
261
  st.sidebar.divider()
262
262
 
263
263
  state_key = "selected_page"
264
+ use_query_params = not IN_DEEPNOTE
264
265
  valid_keys = {p.key for p in pages}
265
- default_key = st.query_params.get("page", pages[0].key)
266
+ if use_query_params:
267
+ default_key = st.query_params.get("page", pages[0].key)
268
+ else:
269
+ default_key = st.session_state.get(state_key, pages[0].key)
266
270
 
267
271
  if default_key not in valid_keys:
268
272
  default_key = pages[0].key
@@ -283,7 +287,13 @@ def sidebar(pages: list[PageSpec]) -> PageSpec:
283
287
  st.session_state[nav_sync_key] = nav_target
284
288
 
285
289
  # Sync from URL only on initial load or when query param changed externally.
286
- if st.session_state.get(nav_sync_key) != default_key:
290
+ # In Deepnote, query params can be unreliable in embedded apps, so we
291
+ # stick to session state instead.
292
+ if use_query_params and st.session_state.get(nav_sync_key) != default_key:
293
+ st.session_state[state_key] = default_key
294
+ st.session_state["navigation_radio"] = default_label
295
+ st.session_state[nav_sync_key] = default_key
296
+ elif not use_query_params and "navigation_radio" not in st.session_state:
287
297
  st.session_state[state_key] = default_key
288
298
  st.session_state["navigation_radio"] = default_label
289
299
  st.session_state[nav_sync_key] = default_key
@@ -310,7 +320,8 @@ def sidebar(pages: list[PageSpec]) -> PageSpec:
310
320
  # Update session state and query params from whatever the radio returned
311
321
  selected_key = options.get(selected_label, pages[0].key)
312
322
  st.session_state[state_key] = selected_key
313
- st.query_params.page = selected_key
323
+ if use_query_params:
324
+ st.query_params["page"] = selected_key
314
325
  st.session_state[nav_sync_key] = selected_key
315
326
 
316
327
  return next(p for p in pages if p.key == selected_key)
@@ -5,7 +5,7 @@
5
5
  requires = [
6
6
  "setuptools == 82.0.1",
7
7
  "setuptools_scm[toml] == 10.0.5",
8
- "frequenz-repo-config[lib] == 0.14.0",
8
+ "frequenz-repo-config[lib] == 0.18.0",
9
9
  ]
10
10
  build-backend = "setuptools.build_meta"
11
11
 
@@ -14,12 +14,12 @@ name = "frequenz-cs-reporting"
14
14
  description = "Streamlit Library for reporting and solar monitoring"
15
15
  readme = "README.md"
16
16
  dynamic = ["version"]
17
- license = { text = "MIT" }
17
+ license = "MIT"
18
+ license-files = ["LICENSE"]
18
19
  keywords = ["frequenz", "python", "lib", "library", "frequenz-cs-reporting", "tooling", "notebooks", "streamlit"]
19
20
  classifiers = [
20
21
  "Development Status :: 3 - Alpha",
21
22
  "Intended Audience :: Developers",
22
- "License :: OSI Approved :: MIT License",
23
23
  "Programming Language :: Python :: 3",
24
24
  "Programming Language :: Python :: 3 :: Only",
25
25
  "Topic :: Software Development :: Libraries",
@@ -51,27 +51,28 @@ email = "floss@frequenz.com"
51
51
  [project.optional-dependencies]
52
52
  dev-flake8 = [
53
53
  "flake8 == 7.3.0",
54
+ "flake8-datetimez == 20.10.0",
54
55
  "flake8-docstrings == 1.7.0",
55
56
  "flake8-pyproject == 1.2.4", # For reading the flake8 config from pyproject.toml
56
- "pydoclint == 0.8.3",
57
+ "pydoclint == 0.8.6",
57
58
  "pydocstyle == 6.3.0",
58
59
  ]
59
- dev-formatting = ["black == 26.3.1", "isort == 8.0.1"]
60
+ dev-formatting = ["black == 26.5.1", "isort == 8.0.1"]
60
61
  dev-mkdocs = [
61
62
  "Markdown == 3.10.2",
62
- "black == 26.3.1",
63
+ "black == 26.5.1",
63
64
  "mike == 2.2.0",
64
65
  "mkdocs-gen-files == 0.6.1",
65
66
  "mkdocs-literate-nav == 0.6.3",
66
67
  "mkdocs-macros-plugin == 1.5.0",
67
68
  "mkdocs-material == 9.7.6",
68
69
  "mkdocstrings[python] == 1.0.4",
69
- "mkdocstrings-python == 2.0.3",
70
- "frequenz-repo-config[lib] == 0.14.0",
70
+ "mkdocstrings-python == 2.0.4",
71
+ "frequenz-repo-config[lib] == 0.18.0",
71
72
  ]
72
73
  dev-mypy = [
73
- "mypy == 1.20.1",
74
- "types-Markdown == 3.10.2.20260211",
74
+ "mypy == 2.1.0",
75
+ "types-Markdown == 3.10.2.20260518",
75
76
  "types-pytz",
76
77
  "pandas-stubs",
77
78
  # For checking the noxfile, docs/ script, and tests
@@ -79,7 +80,7 @@ dev-mypy = [
79
80
  ]
80
81
  dev-noxfile = [
81
82
  "nox == 2026.4.10",
82
- "frequenz-repo-config[lib] == 0.14.0",
83
+ "frequenz-repo-config[lib] == 0.18.0",
83
84
  ]
84
85
  dev-pylint = [
85
86
  # dev-pytest already defines a dependency to pylint because of the examples
@@ -87,11 +88,11 @@ dev-pylint = [
87
88
  "frequenz-cs-reporting[dev-mkdocs,dev-noxfile,dev-pytest]",
88
89
  ]
89
90
  dev-pytest = [
90
- "pytest == 9.0.3",
91
- "pylint == 4.0.5", # We need this to check for the examples
92
- "frequenz-repo-config[extra-lint-examples] == 0.14.0",
91
+ "pytest == 9.1.0",
92
+ "pylint == 4.0.6", # We need this to check for the examples
93
+ "frequenz-repo-config[extra-lint-examples] == 0.18.0",
93
94
  "pytest-mock == 3.15.1",
94
- "pytest-asyncio == 1.3.0",
95
+ "pytest-asyncio == 1.4.0",
95
96
  "async-solipsism == 0.9",
96
97
  ]
97
98
  dev = [
@@ -6,6 +6,7 @@
6
6
  from __future__ import annotations
7
7
 
8
8
  import base64
9
+ import os
9
10
  from dataclasses import dataclass
10
11
  from pathlib import Path
11
12
 
@@ -21,6 +22,10 @@ from frequenz.cs_reporting.ui_resources import (
21
22
  ASSETS_DIR = Path(__file__).resolve().parents[1] / "assets"
22
23
  BACKGROUND_PATH = ASSETS_DIR / "neustrom_background.png"
23
24
  LOGO_PATH = ASSETS_DIR / "neustrom_logo.png"
25
+ IN_DEEPNOTE = any(
26
+ key in os.environ
27
+ for key in ("DEEPNOTE_PROJECT_ID", "DEEPNOTE_WORKSPACE_ID", "DEEPNOTE")
28
+ )
24
29
 
25
30
  # ── Static content data ───────────────────────────────────────────────────────
26
31
 
@@ -102,11 +107,12 @@ def _navigate_to(page_key: str, section: str | None = None) -> None:
102
107
  """Navigate to a target page using the app's query-param routing."""
103
108
  st.session_state["selected_page"] = page_key
104
109
  st.session_state["_nav_target"] = page_key
105
- st.query_params.page = page_key
106
- if section:
107
- st.query_params.section = section
108
- elif "section" in st.query_params:
109
- del st.query_params["section"]
110
+ if not IN_DEEPNOTE:
111
+ st.query_params["page"] = page_key
112
+ if section:
113
+ st.query_params["section"] = section
114
+ elif "section" in st.query_params:
115
+ del st.query_params["section"]
110
116
  st.rerun()
111
117
 
112
118
 
@@ -6,7 +6,7 @@
6
6
  from __future__ import annotations
7
7
 
8
8
  import re
9
- from datetime import date, timedelta
9
+ from datetime import UTC, datetime, timedelta
10
10
 
11
11
  import pandas as pd
12
12
  import streamlit as st
@@ -98,7 +98,7 @@ def render() -> None:
98
98
  st.title("Reporting-Dashboard")
99
99
 
100
100
  # Collect user inputs from sidebar
101
- today = date.today()
101
+ today = datetime.now(tz=UTC).date()
102
102
  selections = collect_sidebar_inputs(
103
103
  default_start=today,
104
104
  default_end=today,
@@ -114,7 +114,7 @@ def render() -> None:
114
114
  start_time = set_date_to_midnight(selections["start_date"], timezone)
115
115
  end_time = normalize_date_for_reporting(selections["end_date"], timezone)
116
116
  end_time = end_time.replace(microsecond=0)
117
- if end_time.date() != date.today():
117
+ if end_time.date() != datetime.now(tz=UTC).date():
118
118
  end_time += timedelta(days=1)
119
119
 
120
120
  # Validate date range
@@ -22,7 +22,9 @@ from frequenz.cs_reporting.services.client_factory import get_microgrid_config
22
22
  from frequenz.cs_reporting.services.solar_workflow import build_workflow_request
23
23
  from frequenz.cs_reporting.views.solar_results import render_workflow_results
24
24
 
25
- DEFAULT_START_DATE = datetime.date(datetime.date.today().year, 1, 1)
25
+ DEFAULT_START_DATE = datetime.date(
26
+ datetime.datetime.now(tz=datetime.timezone.utc).year, 1, 1
27
+ )
26
28
  DEFAULT_RESAMPLE_PERIOD = "3600"
27
29
  DEFAULT_BASELINE_MODELS: list[str] = []
28
30
 
@@ -198,9 +198,13 @@ def plot_percentage_bar(
198
198
  "font_color": "#f8fafc",
199
199
  "bordercolor": "#334155",
200
200
  },
201
+ separators=",.",
201
202
  annotations=[
202
203
  {
203
- "text": f"<b>{total_key}:</b> {total:,.0f} kWh",
204
+ "text": f"<b>{total_key}:</b> {total:,.0f}".replace(",", "X")
205
+ .replace(".", ",")
206
+ .replace("X", ".")
207
+ + " kWh",
204
208
  "x": 0.5,
205
209
  "xref": "paper",
206
210
  "y": 1.35,
@@ -6,7 +6,7 @@
6
6
  from __future__ import annotations
7
7
 
8
8
  from dataclasses import dataclass
9
- from datetime import date, timedelta
9
+ from datetime import UTC, date, datetime, timedelta
10
10
  from typing import Any, Sequence
11
11
 
12
12
  import streamlit as st
@@ -77,7 +77,7 @@ def collect_sidebar_inputs(
77
77
  # Date & Time Section
78
78
  st.subheader("Zeitraum")
79
79
 
80
- today = date.today()
80
+ today = datetime.now(tz=UTC).date()
81
81
  fallback_start = default_start or (today - timedelta(days=7))
82
82
 
83
83
  start_key = f"{key_prefix}start_date"
@@ -96,9 +96,9 @@ def build_workflow_request(
96
96
  metadata, service endpoints, and model configuration.
97
97
  """
98
98
  start_dt = datetime.datetime.combine(
99
- start_date, datetime.datetime.min.time()
100
- ).astimezone(datetime.timezone.utc)
101
- end_timestamp = datetime.datetime.now().astimezone(datetime.timezone.utc)
99
+ start_date, datetime.time.min, tzinfo=datetime.UTC
100
+ )
101
+ end_timestamp = datetime.datetime.now(tz=datetime.UTC)
102
102
 
103
103
  days_since_start = max(0, (end_timestamp - start_dt).days)
104
104
  actual_rolling_duration = min(rolling_view_duration, days_since_start)
@@ -5,7 +5,7 @@
5
5
 
6
6
  from __future__ import annotations
7
7
 
8
- from datetime import date, datetime
8
+ from datetime import UTC, date, datetime
9
9
  from typing import Union
10
10
 
11
11
  from pandas import to_datetime
@@ -34,7 +34,7 @@ def to_iso8601(d: DateLike) -> str:
34
34
  if isinstance(d, datetime):
35
35
  return d.isoformat()
36
36
  if isinstance(d, date):
37
- return datetime(d.year, d.month, d.day).isoformat()
37
+ return datetime(d.year, d.month, d.day, tzinfo=UTC).isoformat()
38
38
  raise TypeError(f"Invalid date-like value: {d!r}")
39
39
 
40
40
 
@@ -51,8 +51,21 @@ def validate_range(start: DateLike, end: DateLike) -> tuple[datetime, datetime]:
51
51
  Raises:
52
52
  ValueError: If ``start`` is greater than or equal to ``end``.
53
53
  """
54
- start_dt = to_datetime(start)
55
- end_dt = to_datetime(end)
54
+ start_ts = to_datetime(start)
55
+ end_ts = to_datetime(end)
56
+
57
+ if start_ts.tzinfo is None:
58
+ start_ts = start_ts.tz_localize(UTC)
59
+ else:
60
+ start_ts = start_ts.tz_convert(UTC)
61
+
62
+ if end_ts.tzinfo is None:
63
+ end_ts = end_ts.tz_localize(UTC)
64
+ else:
65
+ end_ts = end_ts.tz_convert(UTC)
66
+
67
+ start_dt = start_ts.to_pydatetime()
68
+ end_dt = end_ts.to_pydatetime()
56
69
  if end_dt <= start_dt:
57
70
  raise ValueError("end_date must be after start_date")
58
71
  return start_dt, end_dt
@@ -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(
@@ -131,7 +131,7 @@ def render_table_section(
131
131
  header_cols[0].markdown("", unsafe_allow_html=True)
132
132
 
133
133
  if not safe_df.empty:
134
- csv_bytes = display_df.to_csv(index=False).encode("utf-8")
134
+ csv_bytes = display_df.to_csv(index=False, sep=";", decimal=",").encode("utf-8")
135
135
  _style_download_button(header_cols[1])
136
136
  header_cols[1].download_button(
137
137
  label="CSV herunterladen",
@@ -160,7 +160,9 @@ def render_master_df(master_df: pd.DataFrame) -> None:
160
160
  header_cols = st.columns([10, 1])
161
161
  header_cols[0].caption("Standardized master dataframe")
162
162
  display_df = _round_numeric_columns(master_df)
163
- master_csv = display_df.to_csv(index=False).encode("utf-8")
163
+ master_csv = display_df.to_csv(index=False, sep=";", decimal=",").encode(
164
+ "utf-8"
165
+ )
164
166
  _style_download_button(header_cols[1])
165
167
  header_cols[1].download_button(
166
168
  label="Download Gesamt-Datensatz",
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: frequenz-cs-reporting
3
- Version: 0.2.8
3
+ Version: 0.2.10
4
4
  Summary: Streamlit Library for reporting and solar monitoring
5
5
  Author-email: Frequenz Energy-as-a-Service GmbH <floss@frequenz.com>
6
- License: MIT
6
+ License-Expression: MIT
7
7
  Project-URL: Documentation, https://frequenz-floss.github.io/frequenz-cs-reporting/
8
8
  Project-URL: Changelog, https://github.com/frequenz-floss/frequenz-cs-reporting/releases
9
9
  Project-URL: Issues, https://github.com/frequenz-floss/frequenz-cs-reporting/issues
@@ -12,7 +12,6 @@ Project-URL: Support, https://github.com/frequenz-floss/frequenz-cs-reporting/di
12
12
  Keywords: frequenz,python,lib,library,frequenz-cs-reporting,tooling,notebooks,streamlit
13
13
  Classifier: Development Status :: 3 - Alpha
14
14
  Classifier: Intended Audience :: Developers
15
- Classifier: License :: OSI Approved :: MIT License
16
15
  Classifier: Programming Language :: Python :: 3
17
16
  Classifier: Programming Language :: Python :: 3 :: Only
18
17
  Classifier: Topic :: Software Development :: Libraries
@@ -36,41 +35,42 @@ Requires-Dist: streamlit-aggrid>=1.1.8.post1
36
35
  Requires-Dist: watchdog>=6.0.0
37
36
  Provides-Extra: dev-flake8
38
37
  Requires-Dist: flake8==7.3.0; extra == "dev-flake8"
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.8.6; 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.14.0; extra == "dev-mkdocs"
55
+ Requires-Dist: mkdocstrings-python==2.0.4; 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.20260211; 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.14.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.14.0; extra == "dev-pytest"
69
+ Requires-Dist: pytest==9.1.0; 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"
@@ -18,45 +18,46 @@ frequenz-cs-reporting[dev-flake8,dev-formatting,dev-mkdocs,dev-mypy,dev-noxfile,
18
18
 
19
19
  [dev-flake8]
20
20
  flake8==7.3.0
21
+ flake8-datetimez==20.10.0
21
22
  flake8-docstrings==1.7.0
22
23
  flake8-pyproject==1.2.4
23
- pydoclint==0.8.3
24
+ pydoclint==0.8.6
24
25
  pydocstyle==6.3.0
25
26
 
26
27
  [dev-formatting]
27
- black==26.3.1
28
+ black==26.5.1
28
29
  isort==8.0.1
29
30
 
30
31
  [dev-mkdocs]
31
32
  Markdown==3.10.2
32
- black==26.3.1
33
+ black==26.5.1
33
34
  mike==2.2.0
34
35
  mkdocs-gen-files==0.6.1
35
36
  mkdocs-literate-nav==0.6.3
36
37
  mkdocs-macros-plugin==1.5.0
37
38
  mkdocs-material==9.7.6
38
39
  mkdocstrings[python]==1.0.4
39
- mkdocstrings-python==2.0.3
40
- frequenz-repo-config[lib]==0.14.0
40
+ mkdocstrings-python==2.0.4
41
+ frequenz-repo-config[lib]==0.18.0
41
42
 
42
43
  [dev-mypy]
43
- mypy==1.20.1
44
- types-Markdown==3.10.2.20260211
44
+ mypy==2.1.0
45
+ types-Markdown==3.10.2.20260518
45
46
  types-pytz
46
47
  pandas-stubs
47
48
  frequenz-cs-reporting[dev-mkdocs,dev-noxfile,dev-pytest]
48
49
 
49
50
  [dev-noxfile]
50
51
  nox==2026.4.10
51
- frequenz-repo-config[lib]==0.14.0
52
+ frequenz-repo-config[lib]==0.18.0
52
53
 
53
54
  [dev-pylint]
54
55
  frequenz-cs-reporting[dev-mkdocs,dev-noxfile,dev-pytest]
55
56
 
56
57
  [dev-pytest]
57
- pytest==9.0.3
58
- pylint==4.0.5
59
- frequenz-repo-config[extra-lint-examples]==0.14.0
58
+ pytest==9.1.0
59
+ pylint==4.0.6
60
+ frequenz-repo-config[extra-lint-examples]==0.18.0
60
61
  pytest-mock==3.15.1
61
- pytest-asyncio==1.3.0
62
+ pytest-asyncio==1.4.0
62
63
  async-solipsism==0.9
@@ -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
- - Translate the output texts to german.
11
-
12
- ## Bug Fixes