frequenz-cs-reporting 0.5.3__tar.gz → 0.5.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/Dockerfile +0 -1
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/PKG-INFO +1 -1
- frequenz_cs_reporting-0.5.4/RELEASE_NOTES.md +15 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/app_pages/reporting.py +80 -61
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/services/client_factory.py +32 -3
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/services/parquet_cache.py +10 -2
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz_cs_reporting.egg-info/PKG-INFO +1 -1
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz_cs_reporting.egg-info/scm_file_list.json +1 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz_cs_reporting.egg-info/scm_version.json +2 -2
- frequenz_cs_reporting-0.5.3/RELEASE_NOTES.md +0 -17
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/.streamlit/config.toml +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/LICENSE +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/MANIFEST.in +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/README.md +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/app.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/pyproject.toml +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/setup.cfg +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/__init__.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/app_pages/__init__.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/app_pages/battery_optimization.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/app_pages/data_export.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/app_pages/home.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/app_pages/solar.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/assets/__init__.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/assets/neustrom_background.png +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/assets/neustrom_logo.png +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/components/__init__.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/components/inputs.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/components/plot_charts.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/components/sidebar_inputs.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/components/tables.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/components/ui.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/conftest.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/constants.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/content/tooltips.toml +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/py.typed +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/rep_cs_core/__init__.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/rep_cs_core/config.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/rep_cs_core/page_spec.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/services/__init__.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/services/battery_capacity.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/services/battery_optimization_calculations.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/services/cache_config.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/services/component_availability.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/services/data_service.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/services/deepnote_cache.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/services/market_metering.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/services/reporting_session.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/services/solar_workflow.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/services/sync_data.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/styles/dashboard.css +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/styles/data_export.css +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/styles/home.css +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/styles/kpi.css +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/styles/plot_card.css +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/templates/dashboard_section_divider.html +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/templates/home_feature_grid.html +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/templates/home_feature_item.html +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/templates/home_footer.html +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/templates/home_hero.html +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/templates/home_logo.html +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/templates/home_stat_item.html +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/templates/home_stats.html +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/ui_resources.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/utils/__init__.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/utils/env.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/utils/time.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/utils/timing.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/views/__init__.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/views/battery_optimization.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/views/component_sources.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/views/dashboard.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/views/metric_renderers.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/views/plot_renderers.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/views/sections.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/views/solar_results.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/views/table_renderers.py +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz_cs_reporting.egg-info/SOURCES.txt +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz_cs_reporting.egg-info/dependency_links.txt +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz_cs_reporting.egg-info/requires.txt +0 -0
- {frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz_cs_reporting.egg-info/top_level.txt +0 -0
|
@@ -31,7 +31,6 @@ RUN pip install --no-cache-dir \
|
|
|
31
31
|
"kaleido>=0.2.1,<1.2.0" \
|
|
32
32
|
"marshmallow>=4.1.0,<5" \
|
|
33
33
|
"grpcio>=1.81.1,<2" \
|
|
34
|
-
"frequenz-cs-reporting>=0.5.0,<0.6.0"
|
|
35
34
|
|
|
36
35
|
# Prefer packages installed in Deepnote's venv over Deepnote's toolkit server libs.
|
|
37
36
|
ENV PYTHONPATH="/root/venv/lib/python3.12/site-packages:${PYTHONPATH}"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Frequenz CS Reporting Library Release Notes
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Upgrading
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## New Features
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
## Bug Fixes
|
|
13
|
+
|
|
14
|
+
- Reporting data is now loaded directly instead of waiting for a warm cache or a background refresh, so the displayed data is current for the selected range.
|
|
15
|
+
- Battery state-of-charge and grid metering data are fetched in parallel, reducing reporting-page load time.
|
|
@@ -6,8 +6,11 @@
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
8
|
import re
|
|
9
|
-
|
|
9
|
+
import threading
|
|
10
|
+
from collections.abc import Callable
|
|
11
|
+
from concurrent.futures import Future, ThreadPoolExecutor
|
|
10
12
|
from datetime import UTC, datetime, timedelta
|
|
13
|
+
from typing import Any
|
|
11
14
|
|
|
12
15
|
import pandas as pd
|
|
13
16
|
import streamlit as st
|
|
@@ -16,6 +19,7 @@ from frequenz.lib.notebooks.reporting.utils.helpers import (
|
|
|
16
19
|
normalize_date_for_reporting,
|
|
17
20
|
set_date_to_midnight,
|
|
18
21
|
)
|
|
22
|
+
from streamlit.runtime.scriptrunner import add_script_run_ctx, get_script_run_ctx
|
|
19
23
|
|
|
20
24
|
from frequenz.cs_reporting.components.sidebar_inputs import collect_sidebar_inputs
|
|
21
25
|
from frequenz.cs_reporting.rep_cs_core.page_spec import PageSpec
|
|
@@ -26,12 +30,10 @@ from frequenz.cs_reporting.services.client_factory import (
|
|
|
26
30
|
get_microgrid_names,
|
|
27
31
|
)
|
|
28
32
|
from frequenz.cs_reporting.services.data_service import (
|
|
29
|
-
get_cached_microgrid_data,
|
|
30
33
|
get_dataset_sync_metadata,
|
|
31
34
|
get_market_metering_data,
|
|
32
35
|
get_microgrid_data,
|
|
33
36
|
get_microgrid_soc_data,
|
|
34
|
-
start_background_microgrid_data_sync,
|
|
35
37
|
)
|
|
36
38
|
from frequenz.cs_reporting.services.reporting_session import (
|
|
37
39
|
ReportingDataKey,
|
|
@@ -75,21 +77,48 @@ def _scroll_to_section_if_requested() -> None:
|
|
|
75
77
|
)
|
|
76
78
|
|
|
77
79
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
"""Rerun the page when its already-started active-power refresh finishes."""
|
|
83
|
-
if not future.done():
|
|
84
|
-
st.info("Microgrid-Daten werden aktualisiert …")
|
|
85
|
-
return
|
|
80
|
+
def _submit_with_script_context(
|
|
81
|
+
executor: ThreadPoolExecutor, fn: Callable[..., Any], *args: Any, **kwargs: Any
|
|
82
|
+
) -> Future[Any]:
|
|
83
|
+
"""Run ``fn`` in ``executor`` with the current Streamlit script context.
|
|
86
84
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
85
|
+
Streamlit caches and session state need the script context, which worker
|
|
86
|
+
threads do not inherit.
|
|
87
|
+
"""
|
|
88
|
+
ctx = get_script_run_ctx()
|
|
89
|
+
|
|
90
|
+
def run() -> Any:
|
|
91
|
+
if ctx is not None:
|
|
92
|
+
add_script_run_ctx(threading.current_thread(), ctx)
|
|
93
|
+
return fn(*args, **kwargs)
|
|
94
|
+
|
|
95
|
+
return executor.submit(run)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _load_battery_soc(
|
|
99
|
+
microgrid_id: int, start: datetime, end: datetime, resolution: timedelta
|
|
100
|
+
) -> pd.DataFrame:
|
|
101
|
+
"""Load battery SOC data, timing the whole load."""
|
|
102
|
+
with log_duration("reporting.load_battery_soc"):
|
|
103
|
+
return get_microgrid_soc_data(
|
|
104
|
+
microgrid_id=microgrid_id,
|
|
105
|
+
start_date=start,
|
|
106
|
+
end_date=end,
|
|
107
|
+
resolution=resolution,
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _load_market_metering(
|
|
112
|
+
microgrid_id: int, start: datetime, end: datetime, resolution: timedelta
|
|
113
|
+
) -> pd.DataFrame:
|
|
114
|
+
"""Load DSO market-metering data, timing the whole load."""
|
|
115
|
+
with log_duration("reporting.load_market_metering"):
|
|
116
|
+
return get_market_metering_data(
|
|
117
|
+
microgrid_id=microgrid_id,
|
|
118
|
+
start_date=start,
|
|
119
|
+
end_date=end,
|
|
120
|
+
resolution=resolution,
|
|
121
|
+
)
|
|
93
122
|
|
|
94
123
|
|
|
95
124
|
def _parse_resolution(resolution_str: str) -> timedelta:
|
|
@@ -258,7 +287,6 @@ def _render_reporting_page(*, export_only: bool) -> None:
|
|
|
258
287
|
# We extend the fetch end date by 1 day to ensure the API returns data
|
|
259
288
|
# for the full duration of the selected end_time.
|
|
260
289
|
|
|
261
|
-
active_power_sync: Future[pd.DataFrame] | None = None
|
|
262
290
|
try:
|
|
263
291
|
with st.spinner("Microgrid-Daten werden geladen..."):
|
|
264
292
|
with log_duration("reporting.load_component_types"):
|
|
@@ -275,6 +303,8 @@ def _render_reporting_page(*, export_only: bool) -> None:
|
|
|
275
303
|
else:
|
|
276
304
|
st.title(heading)
|
|
277
305
|
snapshot = take_reporting_data_snapshot(data_key)
|
|
306
|
+
soc_future: Future[Any] | None = None
|
|
307
|
+
dso_future: Future[Any] | None = None
|
|
278
308
|
if snapshot is not None:
|
|
279
309
|
df = snapshot.active_power.copy()
|
|
280
310
|
sync_metadata = snapshot.sync_metadata
|
|
@@ -287,31 +317,39 @@ def _render_reporting_page(*, export_only: bool) -> None:
|
|
|
287
317
|
None if snapshot.dso_energy is None else snapshot.dso_energy.copy()
|
|
288
318
|
)
|
|
289
319
|
else:
|
|
320
|
+
# The datasets are independent, so fetch them concurrently.
|
|
321
|
+
executor = ThreadPoolExecutor(max_workers=2)
|
|
322
|
+
soc_future = (
|
|
323
|
+
_submit_with_script_context(
|
|
324
|
+
executor,
|
|
325
|
+
_load_battery_soc,
|
|
326
|
+
microgrid_id,
|
|
327
|
+
previous_start_time,
|
|
328
|
+
end_time,
|
|
329
|
+
resolution,
|
|
330
|
+
)
|
|
331
|
+
if "battery" in component_types
|
|
332
|
+
else None
|
|
333
|
+
)
|
|
334
|
+
dso_future = _submit_with_script_context(
|
|
335
|
+
executor,
|
|
336
|
+
_load_market_metering,
|
|
337
|
+
microgrid_id,
|
|
338
|
+
previous_start_time,
|
|
339
|
+
end_time,
|
|
340
|
+
resolution,
|
|
341
|
+
)
|
|
342
|
+
executor.shutdown(wait=False)
|
|
290
343
|
with log_duration("reporting.load_active_power"):
|
|
291
|
-
df
|
|
344
|
+
df = get_microgrid_data(
|
|
292
345
|
microgrid_id=microgrid_id,
|
|
293
346
|
start_date=previous_start_time,
|
|
294
347
|
end_date=end_time,
|
|
295
348
|
resolution=resolution,
|
|
296
349
|
)
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
microgrid_id=microgrid_id,
|
|
301
|
-
start_date=previous_start_time,
|
|
302
|
-
end_date=end_time,
|
|
303
|
-
resolution=resolution,
|
|
304
|
-
)
|
|
305
|
-
sync_metadata = get_dataset_sync_metadata(
|
|
306
|
-
"active_power", microgrid_id, resolution
|
|
307
|
-
)
|
|
308
|
-
else:
|
|
309
|
-
active_power_sync = start_background_microgrid_data_sync(
|
|
310
|
-
microgrid_id=microgrid_id,
|
|
311
|
-
start_date=previous_start_time,
|
|
312
|
-
end_date=end_time,
|
|
313
|
-
resolution=resolution,
|
|
314
|
-
)
|
|
350
|
+
sync_metadata = get_dataset_sync_metadata(
|
|
351
|
+
"active_power", microgrid_id, resolution
|
|
352
|
+
)
|
|
315
353
|
battery_soc_df = None
|
|
316
354
|
dso_energy_df = None
|
|
317
355
|
if sync_metadata.status == "error":
|
|
@@ -320,30 +358,18 @@ def _render_reporting_page(*, export_only: bool) -> None:
|
|
|
320
358
|
"Die Aktualisierung ist fehlgeschlagen; angezeigt werden die "
|
|
321
359
|
f"zuletzt erfolgreich gespeicherten Daten ({last_sync})."
|
|
322
360
|
)
|
|
323
|
-
if
|
|
361
|
+
if soc_future is not None:
|
|
324
362
|
try:
|
|
325
|
-
|
|
326
|
-
fetched_soc_df = get_microgrid_soc_data(
|
|
327
|
-
microgrid_id=microgrid_id,
|
|
328
|
-
start_date=previous_start_time,
|
|
329
|
-
end_date=end_time,
|
|
330
|
-
resolution=resolution,
|
|
331
|
-
)
|
|
363
|
+
fetched_soc_df = soc_future.result()
|
|
332
364
|
except Exception as exc: # pylint: disable=broad-except
|
|
333
365
|
st.warning(
|
|
334
366
|
f"Batterie-SOC-Daten konnten nicht geladen werden: {exc}"
|
|
335
367
|
)
|
|
336
368
|
else:
|
|
337
369
|
battery_soc_df = None if fetched_soc_df.empty else fetched_soc_df
|
|
338
|
-
if
|
|
370
|
+
if dso_future is not None:
|
|
339
371
|
try:
|
|
340
|
-
|
|
341
|
-
fetched_dso_energy_df = get_market_metering_data(
|
|
342
|
-
microgrid_id=microgrid_id,
|
|
343
|
-
start_date=previous_start_time,
|
|
344
|
-
end_date=end_time,
|
|
345
|
-
resolution=resolution,
|
|
346
|
-
)
|
|
372
|
+
fetched_dso_energy_df = dso_future.result()
|
|
347
373
|
except Exception as exc: # pylint: disable=broad-except
|
|
348
374
|
st.warning(
|
|
349
375
|
"Netzbetreiber-Messdaten konnten nicht geladen werden: "
|
|
@@ -353,7 +379,7 @@ def _render_reporting_page(*, export_only: bool) -> None:
|
|
|
353
379
|
dso_energy_df = (
|
|
354
380
|
None if fetched_dso_energy_df.empty else fetched_dso_energy_df
|
|
355
381
|
)
|
|
356
|
-
if snapshot is None
|
|
382
|
+
if snapshot is None:
|
|
357
383
|
store_reporting_data_snapshot(
|
|
358
384
|
ReportingDataSnapshot(
|
|
359
385
|
key=data_key,
|
|
@@ -399,9 +425,6 @@ def _render_reporting_page(*, export_only: bool) -> None:
|
|
|
399
425
|
resolution,
|
|
400
426
|
)
|
|
401
427
|
if current_master_df.empty:
|
|
402
|
-
if active_power_sync is not None and not active_power_sync.done():
|
|
403
|
-
_poll_active_power_sync(active_power_sync)
|
|
404
|
-
st.stop()
|
|
405
428
|
st.warning(_NO_REPORTING_DATA_WARNING)
|
|
406
429
|
st.stop()
|
|
407
430
|
|
|
@@ -431,10 +454,6 @@ def _render_reporting_page(*, export_only: bool) -> None:
|
|
|
431
454
|
dso_sync_metadata.skipped_market_location_ids
|
|
432
455
|
),
|
|
433
456
|
)
|
|
434
|
-
if active_power_sync is not None and not active_power_sync.done():
|
|
435
|
-
# Rerun once the refresh finishes so the cached-but-stale data shown
|
|
436
|
-
# above is replaced without requiring a second filter click.
|
|
437
|
-
_poll_active_power_sync(active_power_sync)
|
|
438
457
|
if not export_only:
|
|
439
458
|
_scroll_to_section_if_requested()
|
|
440
459
|
|
|
@@ -342,13 +342,15 @@ def _load_assets_config() -> AssetsConfig:
|
|
|
342
342
|
return _load_assets_config_cached(*_microgrid_config_source())
|
|
343
343
|
|
|
344
344
|
|
|
345
|
-
def
|
|
346
|
-
|
|
345
|
+
def _market_location_ids(
|
|
346
|
+
assets_config: AssetsConfig, microgrid_id: int
|
|
347
|
+
) -> tuple[str, ...]:
|
|
348
|
+
"""Return the market-location IDs related to a microgrid."""
|
|
347
349
|
return tuple(
|
|
348
350
|
sorted(
|
|
349
351
|
{
|
|
350
352
|
str(relation.market_location_id)
|
|
351
|
-
for relation in
|
|
353
|
+
for relation in assets_config.relations.values()
|
|
352
354
|
if relation.microgrid_id == microgrid_id
|
|
353
355
|
and relation.market_location_id is not None
|
|
354
356
|
}
|
|
@@ -356,6 +358,33 @@ def get_market_location_ids(microgrid_id: int) -> tuple[str, ...]:
|
|
|
356
358
|
)
|
|
357
359
|
|
|
358
360
|
|
|
361
|
+
def get_market_location_ids(microgrid_id: int) -> tuple[str, ...]:
|
|
362
|
+
"""Return configured market-location IDs associated with a microgrid.
|
|
363
|
+
|
|
364
|
+
Only the TOML files defining the microgrid are loaded first, which avoids
|
|
365
|
+
parsing the whole configuration directory. If they hold no market
|
|
366
|
+
relation for the microgrid, the complete configuration is used instead, in
|
|
367
|
+
case the relations live in a different file.
|
|
368
|
+
"""
|
|
369
|
+
files = _enterprise_config_files(microgrid_id)
|
|
370
|
+
if not files:
|
|
371
|
+
config_files, _ = _microgrid_config_source()
|
|
372
|
+
files = _microgrid_file_index_cached(config_files).get(microgrid_id)
|
|
373
|
+
if files:
|
|
374
|
+
ids = _market_location_ids(
|
|
375
|
+
_load_assets_config_cached(files, get_microgrid_config_revision()),
|
|
376
|
+
microgrid_id,
|
|
377
|
+
)
|
|
378
|
+
if ids:
|
|
379
|
+
return ids
|
|
380
|
+
_logger.warning(
|
|
381
|
+
"No market relation for microgrid %s in %s; loading all configs.",
|
|
382
|
+
microgrid_id,
|
|
383
|
+
files[0],
|
|
384
|
+
)
|
|
385
|
+
return _market_location_ids(_load_assets_config(), microgrid_id)
|
|
386
|
+
|
|
387
|
+
|
|
359
388
|
def get_market_metering_client(timeout: float) -> MarketMeteringApiClient:
|
|
360
389
|
"""Create a Market Metering client for one bounded request."""
|
|
361
390
|
return MarketMeteringApiClient(
|
|
@@ -12,6 +12,7 @@ import os
|
|
|
12
12
|
import tempfile
|
|
13
13
|
import time
|
|
14
14
|
from collections.abc import Callable, Iterator
|
|
15
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
15
16
|
from contextlib import contextmanager
|
|
16
17
|
from dataclasses import asdict, dataclass, field
|
|
17
18
|
from datetime import UTC, datetime, timedelta
|
|
@@ -233,9 +234,16 @@ class ParquetRangeCache:
|
|
|
233
234
|
unavailable_errors: list[Exception] = []
|
|
234
235
|
skipped_market_location_ids: set[str] = set()
|
|
235
236
|
incoming_axis: tuple[str, str | None] | None = None
|
|
236
|
-
|
|
237
|
+
# Fetch all ranges concurrently; results are consumed in order below.
|
|
238
|
+
executor = ThreadPoolExecutor(max_workers=len(ranges))
|
|
239
|
+
futures = [
|
|
240
|
+
executor.submit(self._fetch_with_retry, fetch, start_, end_)
|
|
241
|
+
for start_, end_ in ranges
|
|
242
|
+
]
|
|
243
|
+
executor.shutdown(wait=False)
|
|
244
|
+
for (range_start, range_end), future in zip(ranges, futures):
|
|
237
245
|
try:
|
|
238
|
-
raw_frame =
|
|
246
|
+
raw_frame = future.result()
|
|
239
247
|
except Exception as exc: # pylint: disable=broad-except
|
|
240
248
|
if not (
|
|
241
249
|
key.name.startswith("day_ahead_prices_")
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
# Frequenz CS Reporting Library Release Notes
|
|
2
|
-
|
|
3
|
-
## Summary
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
## Upgrading
|
|
7
|
-
- Add cs-reporting library dependency to the dockerfile.
|
|
8
|
-
|
|
9
|
-
## New Features
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
## Bug Fixes
|
|
13
|
-
|
|
14
|
-
- Background data synchronizations are shared between dashboard sessions and failed synchronizations are throttled before retrying.
|
|
15
|
-
- Hybrid cache persistence now copies data atomically, ignores temporary files, and persists available data when a synchronization fails.
|
|
16
|
-
- Time series plots with many points now render with WebGL, and the battery overview plot shows the grid feed-in as "Netzeinspeisung".
|
|
17
|
-
- Table column choosers are refreshed when the available columns change, e.g. after applying a new date filter.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/conftest.py
RENAMED
|
File without changes
|
{frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/constants.py
RENAMED
|
File without changes
|
|
File without changes
|
{frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/py.typed
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/styles/kpi.css
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/utils/env.py
RENAMED
|
File without changes
|
{frequenz_cs_reporting-0.5.3 → frequenz_cs_reporting-0.5.4}/src/frequenz/cs_reporting/utils/time.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|