Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions documentation/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Infrastructure / Support
Bugfixes
-----------

* In the asset KPI endpoint, select a single deterministic belief per event (``one_deterministic_belief_per_event=True``) so multiple sources reporting beliefs for the same event are not summed together [see `PR #2488 <https://github.com/FlexMeasures/flexmeasures/pull/2488>`_]
* ``flexmeasures add schedule --dry-run`` no longer saves a schedule when it is combined with ``--as-job``, where the flag used to be dropped without a word and the queued job stored its schedule anyway; that combination is now rejected, and a dry run says how many beliefs it would have saved and which events they cover [see `PR #2483 <https://www.github.com/FlexMeasures/flexmeasures/pull/2483>`_]
* Upgrading a database old enough to still carry the pre-``GenericAsset``/``Sensor`` tables now works, where the v0.18.0 migration that removes them crashed twice over: once while checking whether those tables hold data, as soon as one of them held more than a single row, and once while dropping them, because it dropped each table before the ones referencing it [see `PR #2475 <https://www.github.com/FlexMeasures/flexmeasures/pull/2475>`_]
* A forecaster that is told both where to start training and how much history to train on now trains on whichever of the two asks for less data, rather than training back to the start date: ``train-start`` says where training may begin, and ``train-period`` says how much history to use [see `PR #2482 <https://www.github.com/FlexMeasures/flexmeasures/pull/2482>`_]
Expand Down
1 change: 1 addition & 0 deletions flexmeasures/api/v3_0/assets.py
Original file line number Diff line number Diff line change
Expand Up @@ -2345,6 +2345,7 @@ def get_kpis(self, id: int, asset: GenericAsset, start, end):
event_starts_after=start,
event_ends_before=end,
most_recent_beliefs_only=True,
one_deterministic_belief_per_event=True,
)
# Count each event once, under the window it starts in.
# The search also returns events that merely overlap the window, which the chart draws,
Expand Down
63 changes: 63 additions & 0 deletions flexmeasures/api/v3_0/tests/test_assets_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -2070,3 +2070,66 @@ def local_day(day: int) -> str:
assert sum(totals.values()) == pytest.approx(
222.0
), "each event counts once across neighbouring days, not twice"


@pytest.mark.parametrize("requesting_user", ["test_admin_user@seita.nl"], indirect=True)
def test_kpi_two_sources_same_event(
db, client, setup_api_test_data, setup_sources, requesting_user
):
"""When multiple sources report beliefs for the same event, a KPI should not sum across sources."""
asset_type = (
db.session.query(GenericAssetType).filter_by(name="battery").one_or_none()
)
asset = GenericAsset(
name="kpi two sources same event",
generic_asset_type=asset_type,
account_id=requesting_user.account_id,
)
db.session.add(asset)
db.session.flush()
sensor = Sensor(
name="kpi two sources sensor",
generic_asset=asset,
event_resolution=timedelta(days=1),
unit="EUR",
)
db.session.add(sensor)
db.session.flush()
sources = list(setup_sources.values())
a, b = sources[0], sources[-1]
assert a.id != b.id, "this test needs two distinct sources"
window_start = datetime(2030, 3, 15, tzinfo=utc)
db.session.bulk_insert_mappings(
TimedBelief,
[
dict(
event_start=window_start,
belief_horizon=timedelta(days=2),
event_value=100.0,
sensor_id=sensor.id,
source_id=a.id,
cumulative_probability=0.5,
),
dict(
event_start=window_start,
belief_horizon=timedelta(days=1),
event_value=80.0,
sensor_id=sensor.id,
source_id=b.id,
cumulative_probability=0.5,
),
],
)
asset.sensors_to_show_as_kpis = [
{"title": "Daily costs", "sensor": sensor.id, "function": "sum"}
]
db.session.flush()
total = _kpi_total(
client,
asset,
window_start.isoformat(),
(window_start + timedelta(days=1)).isoformat(),
)
# The KPI selects one deterministic belief per event rather than double-counting across sources
assert total in (100.0, 80.0)
assert total != 180.0
Loading