Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4cea3874b | ||
|
|
134f0abb5f |
@@ -31,18 +31,24 @@ _TIBBER_API_URL = "https://api.tibber.com/v1-beta/gql"
|
|||||||
_DEFAULT_TIMEOUT = 15.0
|
_DEFAULT_TIMEOUT = 15.0
|
||||||
|
|
||||||
# GraphQL query to fetch a range of 15-minute price nodes.
|
# GraphQL query to fetch a range of 15-minute price nodes.
|
||||||
# ``priceInfoRange(resolution: QUARTER_HOURLY, first: 96)`` fetches up to
|
# ``priceInfoRange`` is a Relay-style cursor connection over the subscription's
|
||||||
# 96 quarter-hourly slots which covers today + tomorrow (2 × 24 × 4 = 192 max,
|
# entire available price history. With no before/after cursor:
|
||||||
# but the Tibber API typically starts from the current slot and returns at
|
# * ``first: N`` returns the OLDEST N nodes (anchored at the subscription
|
||||||
# most the remaining hours of today plus tomorrow, so 96 is a good cap for
|
# start date) — a fixed window that never advances, so it is WRONG for
|
||||||
# "today + tomorrow").
|
# "current + upcoming" prices.
|
||||||
|
# * ``last: N`` returns the NEWEST N nodes (ending at the latest published
|
||||||
|
# slot: tomorrow 23:45 once day-ahead prices are out, else today 23:45).
|
||||||
|
# We want the newest slots, so we use ``last``. 192 = 2 days × 24 h × 4 slots,
|
||||||
|
# which fully covers the "today + tomorrow" window that GET /api/energy/prices
|
||||||
|
# queries (the earliest of the 192 newest slots reaches back past today 00:00
|
||||||
|
# UTC in either publish state).
|
||||||
_PRICE_RANGE_QUERY = """
|
_PRICE_RANGE_QUERY = """
|
||||||
{
|
{
|
||||||
viewer {
|
viewer {
|
||||||
homes {
|
homes {
|
||||||
id
|
id
|
||||||
currentSubscription {
|
currentSubscription {
|
||||||
priceInfoRange(resolution: QUARTER_HOURLY, first: 96) {
|
priceInfoRange(resolution: QUARTER_HOURLY, last: 192) {
|
||||||
nodes {
|
nodes {
|
||||||
startsAt
|
startsAt
|
||||||
total
|
total
|
||||||
@@ -232,9 +238,11 @@ def fetch_price_range(
|
|||||||
) -> list[PricePoint]:
|
) -> list[PricePoint]:
|
||||||
"""Fetch a range of 15-minute price nodes from the Tibber API.
|
"""Fetch a range of 15-minute price nodes from the Tibber API.
|
||||||
|
|
||||||
Sends the ``priceInfoRange(resolution: QUARTER_HOURLY, first: 96)`` query
|
Sends the ``priceInfoRange(resolution: QUARTER_HOURLY, last: 192)`` query
|
||||||
and parses every returned node into a ``PricePoint``. The number of nodes
|
and parses every returned node into a ``PricePoint``. ``last`` (not
|
||||||
is not assumed — all returned nodes are parsed regardless of count.
|
``first``) is used so the newest slots are returned; ``first`` would anchor
|
||||||
|
at the subscription start date and never advance. The number of nodes is
|
||||||
|
not assumed — all returned nodes are parsed regardless of count.
|
||||||
|
|
||||||
Parameters
|
Parameters
|
||||||
----------
|
----------
|
||||||
|
|||||||
@@ -1263,6 +1263,9 @@ class TestSummarizePrincipleC:
|
|||||||
def test_future_window_counts_0_days(self, energy_db: Session) -> None:
|
def test_future_window_counts_0_days(self, energy_db: Session) -> None:
|
||||||
"""A fully future window (all local dates > today) counts 0 days.
|
"""A fully future window (all local dates > today) counts 0 days.
|
||||||
|
|
||||||
|
Pins ``local_now`` to June 25 2026 noon AMS so the 7/1→8/1 window is
|
||||||
|
genuinely in the future regardless of the actual wall-clock date
|
||||||
|
(mirrors the sibling window tests, which all pin ``local_now``).
|
||||||
Matches table row: 7/1→8/1 (all future) → 0 days.
|
Matches table row: 7/1→8/1 (all future) → 0 days.
|
||||||
"""
|
"""
|
||||||
eff_utc = _ams_midnight(2026, 6, 1)
|
eff_utc = _ams_midnight(2026, 6, 1)
|
||||||
@@ -1270,7 +1273,9 @@ class TestSummarizePrincipleC:
|
|||||||
|
|
||||||
start = _ams_midnight(2026, 7, 1)
|
start = _ams_midnight(2026, 7, 1)
|
||||||
end = _ams_midnight(2026, 8, 1)
|
end = _ams_midnight(2026, 8, 1)
|
||||||
result = self._run_summarize_ams(energy_db, start, end)
|
# Pin local_now to June 25 2026 noon AMS so 7/1→8/1 stays fully future.
|
||||||
|
pinned_now = datetime(2026, 6, 25, 12, 0, 0, tzinfo=_ams())
|
||||||
|
result = self._run_summarize_ams(energy_db, start, end, pinned_now=pinned_now)
|
||||||
|
|
||||||
assert result["fixed_costs"] == 0.0, (
|
assert result["fixed_costs"] == 0.0, (
|
||||||
f"All-future window must count 0 days; got fixed_costs={result['fixed_costs']}"
|
f"All-future window must count 0 days; got fixed_costs={result['fixed_costs']}"
|
||||||
|
|||||||
Reference in New Issue
Block a user