Skip to content

Commit

Permalink
Added method to get flexible rate pricing for a specific day
Browse files Browse the repository at this point in the history
  • Loading branch information
markallanson committed Feb 13, 2021
1 parent 38ee674 commit e04e41f
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 11 deletions.
2 changes: 2 additions & 0 deletions octopus_energy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""Python client for the Octopus Energy RESTful API"""

from .models import (
get_tariff_at,
Address,
Aggregate,
Consumption,
Expand Down Expand Up @@ -49,4 +50,5 @@
"GasMeter",
"PageReference",
"TariffRate",
"get_tariff_at",
]
40 changes: 39 additions & 1 deletion octopus_energy/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
25 changes: 16 additions & 9 deletions octopus_energy/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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,
)
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -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 <[email protected]>"]
license = "MIT"
Expand Down
30 changes: 30 additions & 0 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

0 comments on commit e04e41f

Please sign in to comment.