import re from .energy_price_cap_data import PRICE_CAP, PRICE_CAP_HISTORY from .energy_price_cap_types import EnergyFuel, PaymentMethod, PriceCap ISO_DATE = re.compile(r"[0-9]{4}-[0-9]{2}-[0-9]{2}") FUELS = ("electricity", "gas") METHODS = ("direct-debit", "standard-credit", "prepayment") def price_cap(fuel: EnergyFuel, payment_method: PaymentMethod, on_date: str) -> PriceCap: """The Ofgem price cap unit rate and standing charge in force on ``on_date``.""" if not isinstance(on_date, str) or not ISO_DATE.fullmatch(on_date): raise ValueError('onDate must be an ISO date (YYYY-MM-DD), received "%s"' % (on_date,)) if fuel not in FUELS: raise ValueError('unknown fuel "%s": expected electricity or gas' % (fuel,)) if payment_method not in METHODS: raise ValueError( 'unknown payment method "%s": expected direct-debit, standard-credit or prepayment' % (payment_method,) ) rows = [r for r in PRICE_CAP if r.fuel == fuel and r.payment_method == payment_method] if not rows: raise ValueError( "no price cap figures for %s paid by %s: this version carries GB-average Direct Debit figures only" % (fuel, payment_method) ) row = next((r for r in rows if r.valid_from <= on_date <= r.valid_to), None) if row is None: # A pruned build must not answer an old date with a newer cap. if PRICE_CAP_HISTORY != "full": earliest = min(r.valid_from for r in rows) if on_date < earliest: raise ValueError( "no price cap for %s/%s on %s: this build was installed with history=%s, " "so it only carries periods from %s. Reinstall with history=full for older dates." % (fuel, payment_method, on_date, PRICE_CAP_HISTORY, earliest) ) raise ValueError("no price cap for %s/%s on %s" % (fuel, payment_method, on_date)) return PriceCap( fuel=fuel, payment_method=payment_method, region=row.region, unit_rate=row.unit_rate, standing_charge=row.standing_charge, vat_basis_points=row.vat_basis_points, valid_from=row.valid_from, valid_to=row.valid_to, )