Fix range_days undercounting for the "all time" battery range
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 18s
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 18s
(end - start).days truncates instead of rounding, so "all time" (which
starts at the earliest reading's exact timestamp, rarely local midnight)
could report fewer range_days than days_with_data -- reads backwards
("5 of 4 days have data"). Round the duration up instead; day-aligned
presets and custom ranges are unaffected since they're already exact
multiples of 24h.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
parent
c30165c153
commit
591da31e60
1 changed files with 16 additions and 2 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import math
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from zoneinfo import ZoneInfo
|
from zoneinfo import ZoneInfo
|
||||||
|
|
@ -54,6 +55,19 @@ def _resolve_range(
|
||||||
return _resolve_start(session, days, end), end
|
return _resolve_start(session, days, end), end
|
||||||
|
|
||||||
|
|
||||||
|
def _range_days(start: datetime, end: datetime) -> int:
|
||||||
|
"""Number of calendar days a [start, end) range could span. Rounds up
|
||||||
|
(not truncates) the raw duration, since `start` isn't always exactly at
|
||||||
|
a day boundary -- "all time" starts at the first reading's actual
|
||||||
|
timestamp, which is very rarely local midnight -- and truncating there
|
||||||
|
would make `days_with_data` (a whole-calendar-day count) look bigger
|
||||||
|
than the range it's supposedly a fraction of. A day-aligned preset
|
||||||
|
(`days=N`, or a custom Y-M-D range) already lands on an exact multiple
|
||||||
|
of 24h, so rounding up is a no-op for those and only changes the
|
||||||
|
"all time" case."""
|
||||||
|
return max(1, math.ceil((end - start).total_seconds() / 86400))
|
||||||
|
|
||||||
|
|
||||||
def _bucket_key(local_date, granularity: str) -> str:
|
def _bucket_key(local_date, granularity: str) -> str:
|
||||||
if granularity == "month":
|
if granularity == "month":
|
||||||
return local_date.replace(day=1).isoformat()
|
return local_date.replace(day=1).isoformat()
|
||||||
|
|
@ -215,7 +229,7 @@ def battery_history(
|
||||||
if row.battery_soc_percent is not None
|
if row.battery_soc_percent is not None
|
||||||
]
|
]
|
||||||
days_with_data = len({row.ts.astimezone(BNE).date() for row in rows if row.battery_soc_percent is not None})
|
days_with_data = len({row.ts.astimezone(BNE).date() for row in rows if row.battery_soc_percent is not None})
|
||||||
range_days = max(1, (end - start).days)
|
range_days = _range_days(start, end)
|
||||||
|
|
||||||
points = all_points
|
points = all_points
|
||||||
if len(points) > _MAX_BATTERY_CHART_POINTS:
|
if len(points) > _MAX_BATTERY_CHART_POINTS:
|
||||||
|
|
@ -245,7 +259,7 @@ def battery_value(
|
||||||
rows = get_usage_series(session, start, end)
|
rows = get_usage_series(session, start, end)
|
||||||
current_plan = get_current_plan(session)
|
current_plan = get_current_plan(session)
|
||||||
|
|
||||||
range_days = max(1, (end - start).days)
|
range_days = _range_days(start, end)
|
||||||
battery_rows = [r for r in rows if r.battery_charge_w is not None or r.battery_discharge_w is not None]
|
battery_rows = [r for r in rows if r.battery_charge_w is not None or r.battery_discharge_w is not None]
|
||||||
if not battery_rows or current_plan is None:
|
if not battery_rows or current_plan is None:
|
||||||
return {"available": False, "range_days": range_days}
|
return {"available": False, "range_days": range_days}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue