mirror of
https://github.com/Akkudoktor-EOS/EOS.git
synced 2026-09-10 18:06:37 +00:00
fix: honor Energy-Charts feed-in interval coverage (#1275)
This commit is contained in:
@@ -55,6 +55,25 @@ class FeedInTariffEnergyCharts(FeedInTariffProvider):
|
|||||||
"""Return the unique identifier for the Energy-Charts feed-in tariff provider."""
|
"""Return the unique identifier for the Energy-Charts feed-in tariff provider."""
|
||||||
return "FeedInTariffEnergyCharts"
|
return "FeedInTariffEnergyCharts"
|
||||||
|
|
||||||
|
def _has_complete_published_horizon(
|
||||||
|
self, *, now: pd.Timestamp, resolution_seconds: int
|
||||||
|
) -> bool:
|
||||||
|
"""Return whether stored source data covers all currently published intervals.
|
||||||
|
|
||||||
|
Energy-Charts timestamps identify interval starts. The actual coverage therefore ends one
|
||||||
|
source interval after ``highest_orig_datetime``. Before 14:00, prices through the end of
|
||||||
|
the current day are expected; from 14:00 onward, the following day is expected as well.
|
||||||
|
"""
|
||||||
|
if self.highest_orig_datetime is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
published_days = 1 if now.hour < 14 else 2
|
||||||
|
required_coverage_end = now.normalize() + pd.DateOffset(days=published_days)
|
||||||
|
coverage_end = pd.Timestamp(self.highest_orig_datetime) + pd.Timedelta(
|
||||||
|
seconds=resolution_seconds
|
||||||
|
)
|
||||||
|
return coverage_end >= required_coverage_end
|
||||||
|
|
||||||
def _bidding_zone(self) -> str:
|
def _bidding_zone(self) -> str:
|
||||||
settings = self.config.feedintariff.energycharts
|
settings = self.config.feedintariff.energycharts
|
||||||
if settings is None:
|
if settings is None:
|
||||||
@@ -150,11 +169,8 @@ class FeedInTariffEnergyCharts(FeedInTariffProvider):
|
|||||||
|
|
||||||
The final mapped and processed data is inserted into the sequence as `FeedInTariffDataRecord`.
|
The final mapped and processed data is inserted into the sequence as `FeedInTariffDataRecord`.
|
||||||
"""
|
"""
|
||||||
# New prices are available every day at 14:00
|
# Tomorrow's prices are available every day at 14:00.
|
||||||
now = pd.Timestamp.now(tz=self.config.general.timezone)
|
now = pd.Timestamp.now(tz=self.config.general.timezone)
|
||||||
midnight = now.normalize()
|
|
||||||
hours_ahead = 23 if now.time() < pd.Timestamp("14:00").time() else 47
|
|
||||||
end = midnight + pd.Timedelta(hours=hours_ahead)
|
|
||||||
|
|
||||||
if not self.ems_start_datetime:
|
if not self.ems_start_datetime:
|
||||||
raise ValueError(f"Start DateTime not set: {self.ems_start_datetime}")
|
raise ValueError(f"Start DateTime not set: {self.ems_start_datetime}")
|
||||||
@@ -193,8 +209,10 @@ class FeedInTariffEnergyCharts(FeedInTariffProvider):
|
|||||||
elif force_update:
|
elif force_update:
|
||||||
# Use default start date in case of forced update
|
# Use default start date in case of forced update
|
||||||
needs_update = True
|
needs_update = True
|
||||||
elif end > self.highest_orig_datetime:
|
elif not self._has_complete_published_horizon(
|
||||||
# We got enough history, but still not enough data to prediction end
|
now=now, resolution_seconds=resolution_seconds
|
||||||
|
):
|
||||||
|
# We have enough history, but not every expected source interval.
|
||||||
start_datetime = gross_start_datetime
|
start_datetime = gross_start_datetime
|
||||||
needs_update = True
|
needs_update = True
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -79,6 +79,34 @@ class TestFeedInTariffEnergyCharts:
|
|||||||
|
|
||||||
assert series.iloc[0] == pytest.approx(sample_energycharts_json["price"][0] / 1_000_000)
|
assert series.iloc[0] == pytest.approx(sample_energycharts_json["price"][0] / 1_000_000)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("now", "highest_orig_datetime", "resolution_seconds", "expected"),
|
||||||
|
[
|
||||||
|
("2026-07-23T10:00:00+02:00", "2026-07-23T23:00:00+02:00", 15 * 60, False),
|
||||||
|
("2026-07-23T10:00:00+02:00", "2026-07-23T23:45:00+02:00", 15 * 60, True),
|
||||||
|
("2026-07-23T10:00:00+02:00", "2026-07-23T23:00:00+02:00", 60 * 60, True),
|
||||||
|
("2026-07-23T15:00:00+02:00", "2026-07-24T23:00:00+02:00", 15 * 60, False),
|
||||||
|
("2026-07-23T15:00:00+02:00", "2026-07-24T23:45:00+02:00", 15 * 60, True),
|
||||||
|
(
|
||||||
|
pd.Timestamp("2026-03-28 15:00:00", tz="Europe/Berlin"),
|
||||||
|
"2026-03-29T23:45:00+02:00",
|
||||||
|
15 * 60,
|
||||||
|
True,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_published_horizon_requires_complete_last_interval(
|
||||||
|
self, provider, now, highest_orig_datetime, resolution_seconds, expected
|
||||||
|
):
|
||||||
|
provider.highest_orig_datetime = to_datetime(highest_orig_datetime)
|
||||||
|
|
||||||
|
assert (
|
||||||
|
provider._has_complete_published_horizon(
|
||||||
|
now=pd.Timestamp(now), resolution_seconds=resolution_seconds
|
||||||
|
)
|
||||||
|
is expected
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@patch("requests.get")
|
@patch("requests.get")
|
||||||
def test_request_forecast_uses_feedintariff_bidding_zone(
|
def test_request_forecast_uses_feedintariff_bidding_zone(
|
||||||
|
|||||||
Reference in New Issue
Block a user