From e04e41f644cde31163ca90e1d9e432fdcc54dfeb Mon Sep 17 00:00:00 2001 From: markallanson Date: Sat, 13 Feb 2021 11:37:49 +0000 Subject: [PATCH] Added method to get flexible rate pricing for a specific day --- octopus_energy/__init__.py | 2 ++ octopus_energy/client.py | 40 +++++++++++++++++++++++++++++++++++++- octopus_energy/models.py | 25 +++++++++++++++--------- pyproject.toml | 2 +- tests/test_client.py | 30 ++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 11 deletions(-) diff --git a/octopus_energy/__init__.py b/octopus_energy/__init__.py index 7d37a98..4e78444 100644 --- a/octopus_energy/__init__.py +++ b/octopus_energy/__init__.py @@ -1,6 +1,7 @@ """Python client for the Octopus Energy RESTful API""" from .models import ( + get_tariff_at, Address, Aggregate, Consumption, @@ -49,4 +50,5 @@ "GasMeter", "PageReference", "TariffRate", + "get_tariff_at", ] diff --git a/octopus_energy/client.py b/octopus_energy/client.py index b07c073..918811c 100644 --- a/octopus_energy/client.py +++ b/octopus_energy/client.py @@ -120,7 +120,7 @@ async def get_tariff_cost( tariff_code: The tariff code. tariff_type: The type of energy within the tariff. rate_type: The type of rate. - timestamp: The timestamp + timestamp: The timestamp. Returns: The cost per unit of energy for the requested rate at a point in time. @@ -137,3 +137,41 @@ async def get_tariff_cost( ) rates = tariff_rates_from_response(response) return None if not rates else rates[0] + + async def get_daily_flexible_rate_pricing( + self, + product_code: str, + tariff_code: str, + tariff_type: EnergyTariffType, + rate_type: RateType, + timestamp: datetime, + ) -> List[TariffRate]: + """Gets the cost of a flexible rate tariff for each half hour interval on a specific day. + + Note that this will yield indeterminate/meaningless results for standard but it may work, + OK for "go" tariffs which do change price during the course of the day. + + Args: + product_code: The product code. + tariff_code: The tariff code. + tariff_type: The type of energy within the tariff. + rate_type: The type of rate. + timestamp: The timestamp whose date portion will be used to determine which day to + get the pricing for. The start date will be the truncated date time, and + the end period will be midnight on the truncated date time. + + Returns: + The cost per unit of energy for the requested rate for the day requested. + + """ + period_from = datetime(year=timestamp.year, month=timestamp.month, day=timestamp.day) + response = await self.rest_client.get_tariff_v1( + product_code, + tariff_type, + tariff_code, + rate_type, + period_from=period_from, + period_to=period_from + timedelta(days=1), + ) + rates = tariff_rates_from_response(response) + return rates diff --git a/octopus_energy/models.py b/octopus_energy/models.py index 01290fa..90d5b09 100644 --- a/octopus_energy/models.py +++ b/octopus_energy/models.py @@ -141,15 +141,7 @@ def get_tariff_at(self, timestamp: datetime): """Gets the tariff in effect on a meter at a specific date/time. This automatically takes into account open ended tariffs that have no end.""" - return next( - ( - tariff - for tariff in self.tariffs - if timestamp >= tariff.valid_from - and (not tariff.valid_to or timestamp < tariff.valid_to) - ), - None, - ) + return get_tariff_at(self.tariffs, timestamp) @dataclass @@ -250,3 +242,18 @@ class Aggregate(_DocEnum): "quarter", "Aggregate consumption quarterly", ) + + +def get_tariff_at(tariffs: List[Tariff], timestamp: datetime): + """Gets the tariff in effect on a meter at a specific date/time. + + This automatically takes into account open ended tariffs that have no end.""" + return next( + ( + tariff + for tariff in tariffs + if timestamp >= tariff.valid_from + and (not tariff.valid_to or timestamp < tariff.valid_to) + ), + None, + ) diff --git a/pyproject.toml b/pyproject.toml index e0ae039..4322b7f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "octopus-energy" -version = "0.1.13" +version = "0.1.14" description = "Python client for the Octopus Energy RESTful API" authors = ["Mark Allanson "] license = "MIT" diff --git a/tests/test_client.py b/tests/test_client.py index 920c0b3..b1596a4 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -121,3 +121,33 @@ async def test_get_tariff_cost(self, mock_mapper: Mock, mock_rest_client: Mock): ) with self.subTest("returns the result of mapping"): self.assertIsNotNone(response) + + @does_asyncio + @patch("octopus_energy.client.OctopusEnergyRestClient", autospec=True) + @patch("octopus_energy.client.tariff_rates_from_response", autospec=True) + async def test_get_daily_agile_pricing(self, mock_mapper: Mock, mock_rest_client: Mock): + product_code = "pc" + tariff_code = "tc" + tariff_type = EnergyTariffType.ELECTRICITY + rate_type = RateType.STANDARD_UNIT_RATES + timestamp = datetime.utcnow() + async with OctopusEnergyConsumerClient("") as client: + response = await client.get_daily_flexible_rate_pricing( + product_code, tariff_code, tariff_type, rate_type, timestamp + ) + with self.subTest("calls get_tariff_v1 on rest client"): + mock_rest_client.return_value.get_tariff_v1.assert_called_with( + product_code, + tariff_type, + tariff_code, + rate_type, + period_from=datetime( + year=timestamp.year, month=timestamp.month, day=timestamp.day + ), + period_to=datetime( + year=timestamp.year, month=timestamp.month, day=timestamp.day + ) + + timedelta(days=1), + ) + with self.subTest("returns the result of mapping"): + self.assertIsNotNone(response)