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
|
||||
|
||||
import math
|
||||
from collections import defaultdict
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from zoneinfo import ZoneInfo
|
||||
|
|
@ -54,6 +55,19 @@ def _resolve_range(
|
|||
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:
|
||||
if granularity == "month":
|
||||
return local_date.replace(day=1).isoformat()
|
||||
|
|
@ -215,7 +229,7 @@ def battery_history(
|
|||
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
|
||||
if len(points) > _MAX_BATTERY_CHART_POINTS:
|
||||
|
|
@ -245,7 +259,7 @@ def battery_value(
|
|||
rows = get_usage_series(session, start, end)
|
||||
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]
|
||||
if not battery_rows or current_plan is None:
|
||||
return {"available": False, "range_days": range_days}
|
||||
|
|
|
|||
Loading…
Reference in a new issue