Include end time of statistics data points in API response (#56063)

* Include end time of statistics data points in API response

* Correct typing

* Update tests
pull/56170/head
Erik Montnemery 2021-09-13 10:02:24 +02:00 committed by GitHub
parent 7472fb2049
commit d2a9f7904a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 87 additions and 19 deletions

View File

@ -4,7 +4,7 @@ from __future__ import annotations
from datetime import datetime
import json
import logging
from typing import TypedDict
from typing import TypedDict, overload
from sqlalchemy import (
Boolean,
@ -391,6 +391,16 @@ class StatisticsRuns(Base): # type: ignore
)
@overload
def process_timestamp(ts: None) -> None:
...
@overload
def process_timestamp(ts: datetime) -> datetime:
...
def process_timestamp(ts):
"""Process a timestamp into datetime object."""
if ts is None:
@ -401,6 +411,16 @@ def process_timestamp(ts):
return dt_util.as_utc(ts)
@overload
def process_timestamp_to_utc_isoformat(ts: None) -> None:
...
@overload
def process_timestamp_to_utc_isoformat(ts: datetime) -> str:
...
def process_timestamp_to_utc_isoformat(ts: datetime | None) -> str | None:
"""Process a timestamp into UTC isotime."""
if ts is None:

View File

@ -32,6 +32,7 @@ from .models import (
Statistics,
StatisticsMeta,
StatisticsRuns,
process_timestamp,
process_timestamp_to_utc_isoformat,
)
from .util import execute, retryable_database_job, session_scope
@ -437,9 +438,6 @@ def _sorted_statistics_to_dict(
for stat_id in statistic_ids:
result[stat_id] = []
# Called in a tight loop so cache the function here
_process_timestamp_to_utc_isoformat = process_timestamp_to_utc_isoformat
# Append all statistic entries, and do unit conversion
for meta_id, group in groupby(stats, lambda stat: stat.metadata_id): # type: ignore
unit = metadata[meta_id]["unit_of_measurement"]
@ -450,20 +448,25 @@ def _sorted_statistics_to_dict(
else:
convert = no_conversion
ent_results = result[meta_id]
ent_results.extend(
for db_state in group:
start = process_timestamp(db_state.start)
end = start + timedelta(hours=1)
ent_results.append(
{
"statistic_id": statistic_id,
"start": _process_timestamp_to_utc_isoformat(db_state.start),
"start": start.isoformat(),
"end": end.isoformat(),
"mean": convert(db_state.mean, units),
"min": convert(db_state.min, units),
"max": convert(db_state.max, units),
"last_reset": _process_timestamp_to_utc_isoformat(db_state.last_reset),
"last_reset": process_timestamp_to_utc_isoformat(
db_state.last_reset
),
"state": convert(db_state.state, units),
"sum": (_sum := convert(db_state.sum, units)),
"sum_increase": (inc := convert(db_state.sum_increase, units)),
"sum_decrease": None if _sum is None or inc is None else inc - _sum,
}
for db_state in group
)
# Filter out the empty lists if some states had 0 results.

View File

@ -908,6 +908,7 @@ async def test_statistics_during_period(
{
"statistic_id": "sensor.test",
"start": now.isoformat(),
"end": (now + timedelta(hours=1)).isoformat(),
"mean": approx(value),
"min": approx(value),
"max": approx(value),

View File

@ -45,6 +45,7 @@ def test_compile_hourly_statistics(hass_recorder):
expected_1 = {
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"mean": approx(14.915254237288135),
"min": approx(10.0),
"max": approx(20.0),
@ -57,6 +58,7 @@ def test_compile_hourly_statistics(hass_recorder):
expected_2 = {
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(four),
"end": process_timestamp_to_utc_isoformat(four + timedelta(hours=1)),
"mean": approx(20.0),
"min": approx(20.0),
"max": approx(20.0),
@ -164,6 +166,7 @@ def test_compile_hourly_statistics_exception(
expected_1 = {
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(now),
"end": process_timestamp_to_utc_isoformat(now + timedelta(hours=1)),
"mean": None,
"min": None,
"max": None,
@ -176,6 +179,7 @@ def test_compile_hourly_statistics_exception(
expected_2 = {
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(now + timedelta(hours=1)),
"end": process_timestamp_to_utc_isoformat(now + timedelta(hours=2)),
"mean": None,
"min": None,
"max": None,
@ -235,6 +239,7 @@ def test_rename_entity(hass_recorder):
expected_1 = {
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"mean": approx(14.915254237288135),
"min": approx(10.0),
"max": approx(20.0),

View File

@ -95,6 +95,7 @@ def test_compile_hourly_statistics(
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"mean": approx(mean),
"min": approx(min),
"max": approx(max),
@ -159,6 +160,7 @@ def test_compile_hourly_statistics_unsupported(hass_recorder, caplog, attributes
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"mean": approx(13.050847),
"min": approx(-10.0),
"max": approx(30.0),
@ -173,6 +175,7 @@ def test_compile_hourly_statistics_unsupported(hass_recorder, caplog, attributes
{
"statistic_id": "sensor.test6",
"start": process_timestamp_to_utc_isoformat(zero),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"mean": approx(13.050847),
"min": approx(-10.0),
"max": approx(30.0),
@ -187,6 +190,7 @@ def test_compile_hourly_statistics_unsupported(hass_recorder, caplog, attributes
{
"statistic_id": "sensor.test7",
"start": process_timestamp_to_utc_isoformat(zero),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"mean": approx(13.050847),
"min": approx(-10.0),
"max": approx(30.0),
@ -260,6 +264,7 @@ def test_compile_hourly_sum_statistics_amount(
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"max": None,
"mean": None,
"min": None,
@ -272,6 +277,7 @@ def test_compile_hourly_sum_statistics_amount(
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=2)),
"max": None,
"mean": None,
"min": None,
@ -284,6 +290,7 @@ def test_compile_hourly_sum_statistics_amount(
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero + timedelta(hours=2)),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=3)),
"max": None,
"mean": None,
"min": None,
@ -360,6 +367,7 @@ def test_compile_hourly_sum_statistics_amount_reset_every_state_change(
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"max": None,
"mean": None,
"min": None,
@ -426,6 +434,7 @@ def test_compile_hourly_sum_statistics_nan_inf_state(
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"max": None,
"mean": None,
"min": None,
@ -492,6 +501,7 @@ def test_compile_hourly_sum_statistics_total_no_reset(
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"max": None,
"mean": None,
"min": None,
@ -504,6 +514,7 @@ def test_compile_hourly_sum_statistics_total_no_reset(
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=2)),
"max": None,
"mean": None,
"min": None,
@ -516,6 +527,7 @@ def test_compile_hourly_sum_statistics_total_no_reset(
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero + timedelta(hours=2)),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=3)),
"max": None,
"mean": None,
"min": None,
@ -578,6 +590,7 @@ def test_compile_hourly_sum_statistics_total_increasing(
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"max": None,
"mean": None,
"min": None,
@ -590,6 +603,7 @@ def test_compile_hourly_sum_statistics_total_increasing(
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=2)),
"max": None,
"mean": None,
"min": None,
@ -602,6 +616,7 @@ def test_compile_hourly_sum_statistics_total_increasing(
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero + timedelta(hours=2)),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=3)),
"max": None,
"mean": None,
"min": None,
@ -675,6 +690,7 @@ def test_compile_hourly_sum_statistics_total_increasing_small_dip(
"last_reset": None,
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"max": None,
"mean": None,
"min": None,
@ -687,6 +703,7 @@ def test_compile_hourly_sum_statistics_total_increasing_small_dip(
"last_reset": None,
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=2)),
"max": None,
"mean": None,
"min": None,
@ -699,6 +716,7 @@ def test_compile_hourly_sum_statistics_total_increasing_small_dip(
"last_reset": None,
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero + timedelta(hours=2)),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=3)),
"max": None,
"mean": None,
"min": None,
@ -767,6 +785,7 @@ def test_compile_hourly_energy_statistics_unsupported(hass_recorder, caplog):
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"max": None,
"mean": None,
"min": None,
@ -779,6 +798,7 @@ def test_compile_hourly_energy_statistics_unsupported(hass_recorder, caplog):
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=2)),
"max": None,
"mean": None,
"min": None,
@ -791,6 +811,7 @@ def test_compile_hourly_energy_statistics_unsupported(hass_recorder, caplog):
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero + timedelta(hours=2)),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=3)),
"max": None,
"mean": None,
"min": None,
@ -856,6 +877,7 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog):
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"max": None,
"mean": None,
"min": None,
@ -868,6 +890,7 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog):
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=2)),
"max": None,
"mean": None,
"min": None,
@ -880,6 +903,7 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog):
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero + timedelta(hours=2)),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=3)),
"max": None,
"mean": None,
"min": None,
@ -894,6 +918,7 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog):
{
"statistic_id": "sensor.test2",
"start": process_timestamp_to_utc_isoformat(zero),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"max": None,
"mean": None,
"min": None,
@ -906,6 +931,7 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog):
{
"statistic_id": "sensor.test2",
"start": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=2)),
"max": None,
"mean": None,
"min": None,
@ -918,6 +944,7 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog):
{
"statistic_id": "sensor.test2",
"start": process_timestamp_to_utc_isoformat(zero + timedelta(hours=2)),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=3)),
"max": None,
"mean": None,
"min": None,
@ -932,6 +959,7 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog):
{
"statistic_id": "sensor.test3",
"start": process_timestamp_to_utc_isoformat(zero),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"max": None,
"mean": None,
"min": None,
@ -944,6 +972,7 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog):
{
"statistic_id": "sensor.test3",
"start": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=2)),
"max": None,
"mean": None,
"min": None,
@ -956,6 +985,7 @@ def test_compile_hourly_energy_statistics_multiple(hass_recorder, caplog):
{
"statistic_id": "sensor.test3",
"start": process_timestamp_to_utc_isoformat(zero + timedelta(hours=2)),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=3)),
"max": None,
"mean": None,
"min": None,
@ -1011,6 +1041,7 @@ def test_compile_hourly_statistics_unchanged(
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(four),
"end": process_timestamp_to_utc_isoformat(four + timedelta(hours=1)),
"mean": approx(value),
"min": approx(value),
"max": approx(value),
@ -1045,6 +1076,7 @@ def test_compile_hourly_statistics_partially_unavailable(hass_recorder, caplog):
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"mean": approx(21.1864406779661),
"min": approx(10.0),
"max": approx(25.0),
@ -1104,6 +1136,7 @@ def test_compile_hourly_statistics_unavailable(
{
"statistic_id": "sensor.test2",
"start": process_timestamp_to_utc_isoformat(four),
"end": process_timestamp_to_utc_isoformat(four + timedelta(hours=1)),
"mean": approx(value),
"min": approx(value),
"max": approx(value),
@ -1256,6 +1289,7 @@ def test_compile_hourly_statistics_changing_units_1(
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"mean": approx(mean),
"min": approx(min),
"max": approx(max),
@ -1284,6 +1318,7 @@ def test_compile_hourly_statistics_changing_units_1(
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"mean": approx(mean),
"min": approx(min),
"max": approx(max),
@ -1391,6 +1426,7 @@ def test_compile_hourly_statistics_changing_units_3(
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"mean": approx(mean),
"min": approx(min),
"max": approx(max),
@ -1417,6 +1453,7 @@ def test_compile_hourly_statistics_changing_units_3(
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"mean": approx(mean),
"min": approx(min),
"max": approx(max),
@ -1497,6 +1534,7 @@ def test_compile_hourly_statistics_changing_statistics(
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"mean": approx(mean),
"min": approx(min),
"max": approx(max),
@ -1509,6 +1547,7 @@ def test_compile_hourly_statistics_changing_statistics(
{
"statistic_id": "sensor.test1",
"start": process_timestamp_to_utc_isoformat(zero + timedelta(hours=1)),
"end": process_timestamp_to_utc_isoformat(zero + timedelta(hours=2)),
"mean": None,
"min": None,
"max": None,