Skip to content
Merged
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
11 changes: 11 additions & 0 deletions packages/modules/common/configurable_tariff.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,17 @@ def _calculate_next_query_time(self, latest_price_timestamp: datetime) -> None:
next_query_time = (now.replace(hour=next_hour, minute=0, second=0, microsecond=0) +
timedelta(days=day_offset, minutes=random.randint(1, 7) * -5))
if latest_price_timestamp < next_query_time:
tomorrow_start = (now + timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0)
warning_hour = min(self.tariff_update_hours) + LATE_WARNING_OFFSET_HOURS
if latest_price_timestamp < tomorrow_start and now.hour < warning_hour:
# Noch keine Preise für den Folgetag und die Warnschwelle ist noch nicht erreicht:
# kurzer Backoff statt auf das Ende der heute schon bekannten Preise zu springen.
# Sonst würde bei Anbietern mit rollierender Tagesantwort (Preise reichen jeden
# Abruf etwas weiter in den noch laufenden Tag, ohne je den Folgetag zu erreichen)
# der Abruf für Stunden pausiert, noch bevor überhaupt die Warnschwelle erreicht ist
# (siehe #3703).
self._delay_next_query()
return
# Falls die Preise nicht bis zum nächsten geplanten Abruf gültig sind,
# den Abruf auf die Gültigkeitsdauer der Preise setzen
log.debug(f"Die Preise sind nur bis {latest_price_timestamp} gültig, "
Expand Down
50 changes: 47 additions & 3 deletions packages/modules/common/configurable_tariff_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,8 +160,9 @@ def now(cls, tz: Optional[tzinfo] = None):


def test_calculate_next_query_time_uses_latest_price_timestamp(monkeypatch: pytest.MonkeyPatch):
# setup
fixed_now = datetime(2022, 5, 16, 10, 10, 0)
# Preise reichen bereits in den Folgetag, enden dort aber vor dem naechsten geplanten Abruf -
# der Abruf darf auf das Ende der bekannten (Folgetags-)Preise vorgezogen werden.
fixed_now = datetime(2022, 5, 16, 15, 10, 0)

class FixedDateTime(datetime):
@classmethod
Expand All @@ -174,7 +175,50 @@ def now(cls, tz: Optional[tzinfo] = None):
tariff = ConfigurableFlexibleTariff(AwattarTariff(), Mock(return_value=Mock()))
tariff.tariff_update_hours = [14]

latest_price_timestamp = datetime(2022, 5, 16, 12, 30, 0)
latest_price_timestamp = datetime(2022, 5, 17, 2, 30, 0)

# execution
tariff._calculate_next_query_time(latest_price_timestamp) # pyright: ignore[reportPrivateUsage]

# evaluation
expected_timestamp = int(latest_price_timestamp.timestamp())
assert tariff.get.next_query_time == expected_timestamp


def test_calculate_next_query_time_same_day_rollover_before_late_hour_schedules_short_backoff(
monkeypatch: pytest.MonkeyPatch):
# Regression-Test für #3703: Preise reichen nur bis spaeter am selben Tag (noch kein Folgetag),
# Warnschwelle noch nicht erreicht - kurzer Backoff statt Sprung auf das Tagesende, sonst pausiert
# der Abruf bei Anbietern mit rollierender Tagesantwort stundenlang, bevor ueberhaupt gewarnt wurde.
fixed_now = datetime(2022, 5, 16, 15, 10, 0)
_patch_now(monkeypatch, fixed_now)
monkeypatch.setattr(configurable_tariff.random, "randint", Mock(return_value=1))

tariff = ConfigurableFlexibleTariff(AwattarTariff(), Mock(return_value=Mock()))
tariff.tariff_update_hours = [14]

latest_price_timestamp = datetime(2022, 5, 16, 23, 45, 0)

# execution
tariff._calculate_next_query_time(latest_price_timestamp) # pyright: ignore[reportPrivateUsage]

# evaluation
expected_timestamp = int(fixed_now.timestamp()) + RETRY_BACKOFF_SECONDS
assert tariff.get.next_query_time == expected_timestamp


def test_calculate_next_query_time_same_day_rollover_after_late_hour_still_uses_clamp(
monkeypatch: pytest.MonkeyPatch):
# Nach der Warnschwelle ist das Tagesende als naechster Abruf wieder in Ordnung - es wurde ja
# bereits gewarnt, staendiges Nachfragen bringt nichts mehr.
fixed_now = datetime(2022, 5, 16, 16, 10, 0)
_patch_now(monkeypatch, fixed_now)
monkeypatch.setattr(configurable_tariff.random, "randint", Mock(return_value=1))

tariff = ConfigurableFlexibleTariff(AwattarTariff(), Mock(return_value=Mock()))
tariff.tariff_update_hours = [14]

latest_price_timestamp = datetime(2022, 5, 16, 23, 45, 0)

# execution
tariff._calculate_next_query_time(latest_price_timestamp) # pyright: ignore[reportPrivateUsage]
Expand Down
Loading