mirror of
https://github.com/home-assistant/core.git
synced 2025-07-26 06:37:52 +00:00
Improve isoformat timestamp performance (#36991)
* adj * time_fired_isoformat * remove unused code * tests for processing timestamps * restore missing import lost in merge conflict * test for None case
This commit is contained in:
parent
5446641f09
commit
53a91ece4e
@ -13,7 +13,11 @@ import voluptuous as vol
|
|||||||
|
|
||||||
from homeassistant.components import recorder
|
from homeassistant.components import recorder
|
||||||
from homeassistant.components.http import HomeAssistantView
|
from homeassistant.components.http import HomeAssistantView
|
||||||
from homeassistant.components.recorder.models import States, process_timestamp
|
from homeassistant.components.recorder.models import (
|
||||||
|
States,
|
||||||
|
process_timestamp,
|
||||||
|
process_timestamp_to_utc_isoformat,
|
||||||
|
)
|
||||||
from homeassistant.components.recorder.util import execute, session_scope
|
from homeassistant.components.recorder.util import execute, session_scope
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_HIDDEN,
|
ATTR_HIDDEN,
|
||||||
@ -318,7 +322,7 @@ def _sorted_states_to_json(
|
|||||||
|
|
||||||
# Called in a tight loop so cache the function
|
# Called in a tight loop so cache the function
|
||||||
# here
|
# here
|
||||||
_process_timestamp = process_timestamp
|
_process_timestamp_to_utc_isoformat = process_timestamp_to_utc_isoformat
|
||||||
|
|
||||||
# Append all changes to it
|
# Append all changes to it
|
||||||
for ent_id, group in groupby(states, lambda state: state.entity_id):
|
for ent_id, group in groupby(states, lambda state: state.entity_id):
|
||||||
@ -362,9 +366,9 @@ def _sorted_states_to_json(
|
|||||||
ent_results.append(
|
ent_results.append(
|
||||||
{
|
{
|
||||||
STATE_KEY: db_state.state,
|
STATE_KEY: db_state.state,
|
||||||
LAST_CHANGED_KEY: _process_timestamp(
|
LAST_CHANGED_KEY: _process_timestamp_to_utc_isoformat(
|
||||||
db_state.last_changed
|
db_state.last_changed
|
||||||
).isoformat(),
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
prev_state = db_state
|
prev_state = db_state
|
||||||
|
@ -11,7 +11,12 @@ import voluptuous as vol
|
|||||||
|
|
||||||
from homeassistant.components import sun
|
from homeassistant.components import sun
|
||||||
from homeassistant.components.http import HomeAssistantView
|
from homeassistant.components.http import HomeAssistantView
|
||||||
from homeassistant.components.recorder.models import Events, States, process_timestamp
|
from homeassistant.components.recorder.models import (
|
||||||
|
Events,
|
||||||
|
States,
|
||||||
|
process_timestamp,
|
||||||
|
process_timestamp_to_utc_isoformat,
|
||||||
|
)
|
||||||
from homeassistant.components.recorder.util import (
|
from homeassistant.components.recorder.util import (
|
||||||
QUERY_RETRY_WAIT,
|
QUERY_RETRY_WAIT,
|
||||||
RETRIES,
|
RETRIES,
|
||||||
@ -248,7 +253,7 @@ def humanify(hass, events, entity_attr_cache, prev_states=None):
|
|||||||
if event.event_type in external_events:
|
if event.event_type in external_events:
|
||||||
domain, describe_event = external_events[event.event_type]
|
domain, describe_event = external_events[event.event_type]
|
||||||
data = describe_event(event)
|
data = describe_event(event)
|
||||||
data["when"] = event.time_fired
|
data["when"] = event.time_fired_isoformat
|
||||||
data["domain"] = domain
|
data["domain"] = domain
|
||||||
data["context_user_id"] = event.context_user_id
|
data["context_user_id"] = event.context_user_id
|
||||||
yield data
|
yield data
|
||||||
@ -275,7 +280,7 @@ def humanify(hass, events, entity_attr_cache, prev_states=None):
|
|||||||
) or split_entity_id(entity_id)[1].replace("_", " ")
|
) or split_entity_id(entity_id)[1].replace("_", " ")
|
||||||
|
|
||||||
yield {
|
yield {
|
||||||
"when": event.time_fired,
|
"when": event.time_fired_isoformat,
|
||||||
"name": name,
|
"name": name,
|
||||||
"message": _entry_message_from_event(
|
"message": _entry_message_from_event(
|
||||||
hass, entity_id, domain, event, entity_attr_cache
|
hass, entity_id, domain, event, entity_attr_cache
|
||||||
@ -290,7 +295,7 @@ def humanify(hass, events, entity_attr_cache, prev_states=None):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
yield {
|
yield {
|
||||||
"when": event.time_fired,
|
"when": event.time_fired_isoformat,
|
||||||
"name": "Home Assistant",
|
"name": "Home Assistant",
|
||||||
"message": "started",
|
"message": "started",
|
||||||
"domain": HA_DOMAIN,
|
"domain": HA_DOMAIN,
|
||||||
@ -304,7 +309,7 @@ def humanify(hass, events, entity_attr_cache, prev_states=None):
|
|||||||
action = "stopped"
|
action = "stopped"
|
||||||
|
|
||||||
yield {
|
yield {
|
||||||
"when": event.time_fired,
|
"when": event.time_fired_isoformat,
|
||||||
"name": "Home Assistant",
|
"name": "Home Assistant",
|
||||||
"message": action,
|
"message": action,
|
||||||
"domain": HA_DOMAIN,
|
"domain": HA_DOMAIN,
|
||||||
@ -322,7 +327,7 @@ def humanify(hass, events, entity_attr_cache, prev_states=None):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
yield {
|
yield {
|
||||||
"when": event.time_fired,
|
"when": event.time_fired_isoformat,
|
||||||
"name": event_data.get(ATTR_NAME),
|
"name": event_data.get(ATTR_NAME),
|
||||||
"message": event_data.get(ATTR_MESSAGE),
|
"message": event_data.get(ATTR_MESSAGE),
|
||||||
"domain": domain,
|
"domain": domain,
|
||||||
@ -601,6 +606,7 @@ class LazyEventPartialState:
|
|||||||
"_row",
|
"_row",
|
||||||
"_event_data",
|
"_event_data",
|
||||||
"_time_fired",
|
"_time_fired",
|
||||||
|
"_time_fired_isoformat",
|
||||||
"_attributes",
|
"_attributes",
|
||||||
"event_type",
|
"event_type",
|
||||||
"entity_id",
|
"entity_id",
|
||||||
@ -613,6 +619,7 @@ class LazyEventPartialState:
|
|||||||
self._row = row
|
self._row = row
|
||||||
self._event_data = None
|
self._event_data = None
|
||||||
self._time_fired = None
|
self._time_fired = None
|
||||||
|
self._time_fired_isoformat = None
|
||||||
self._attributes = None
|
self._attributes = None
|
||||||
self.event_type = self._row.event_type
|
self.event_type = self._row.event_type
|
||||||
self.entity_id = self._row.entity_id
|
self.entity_id = self._row.entity_id
|
||||||
@ -662,6 +669,18 @@ class LazyEventPartialState:
|
|||||||
)
|
)
|
||||||
return self._time_fired
|
return self._time_fired
|
||||||
|
|
||||||
|
@property
|
||||||
|
def time_fired_isoformat(self):
|
||||||
|
"""Time event was fired in utc isoformat."""
|
||||||
|
if not self._time_fired_isoformat:
|
||||||
|
if self._time_fired:
|
||||||
|
self._time_fired_isoformat = self._time_fired.isoformat()
|
||||||
|
else:
|
||||||
|
self._time_fired_isoformat = process_timestamp_to_utc_isoformat(
|
||||||
|
self._row.time_fired or dt_util.utcnow()
|
||||||
|
)
|
||||||
|
return self._time_fired_isoformat
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def has_old_and_new_state(self):
|
def has_old_and_new_state(self):
|
||||||
"""Check the json data to see if new_state and old_state is present without decoding."""
|
"""Check the json data to see if new_state and old_state is present without decoding."""
|
||||||
|
@ -28,7 +28,7 @@ SCHEMA_VERSION = 8
|
|||||||
|
|
||||||
_LOGGER = logging.getLogger(__name__)
|
_LOGGER = logging.getLogger(__name__)
|
||||||
|
|
||||||
DB_TIMEZONE = "Z"
|
DB_TIMEZONE = "+00:00"
|
||||||
|
|
||||||
|
|
||||||
class Events(Base): # type: ignore
|
class Events(Base): # type: ignore
|
||||||
@ -202,3 +202,13 @@ def process_timestamp(ts):
|
|||||||
return ts.replace(tzinfo=dt_util.UTC)
|
return ts.replace(tzinfo=dt_util.UTC)
|
||||||
|
|
||||||
return dt_util.as_utc(ts)
|
return dt_util.as_utc(ts)
|
||||||
|
|
||||||
|
|
||||||
|
def process_timestamp_to_utc_isoformat(ts):
|
||||||
|
"""Process a timestamp into UTC isotime."""
|
||||||
|
if ts is None:
|
||||||
|
return None
|
||||||
|
if ts.tzinfo is None:
|
||||||
|
return f"{ts.isoformat()}{DB_TIMEZONE}"
|
||||||
|
|
||||||
|
return dt_util.as_utc(ts).isoformat()
|
||||||
|
@ -13,6 +13,7 @@ import voluptuous as vol
|
|||||||
from homeassistant.components import logbook, recorder, sun
|
from homeassistant.components import logbook, recorder, sun
|
||||||
from homeassistant.components.alexa.smart_home import EVENT_ALEXA_SMART_HOME
|
from homeassistant.components.alexa.smart_home import EVENT_ALEXA_SMART_HOME
|
||||||
from homeassistant.components.automation import EVENT_AUTOMATION_TRIGGERED
|
from homeassistant.components.automation import EVENT_AUTOMATION_TRIGGERED
|
||||||
|
from homeassistant.components.recorder.models import process_timestamp_to_utc_isoformat
|
||||||
from homeassistant.components.script import EVENT_SCRIPT_STARTED
|
from homeassistant.components.script import EVENT_SCRIPT_STARTED
|
||||||
from homeassistant.const import (
|
from homeassistant.const import (
|
||||||
ATTR_ENTITY_ID,
|
ATTR_ENTITY_ID,
|
||||||
@ -1230,7 +1231,7 @@ class TestComponentLogbook(unittest.TestCase):
|
|||||||
):
|
):
|
||||||
"""Assert an entry is what is expected."""
|
"""Assert an entry is what is expected."""
|
||||||
if when:
|
if when:
|
||||||
assert when == entry["when"]
|
assert when.isoformat() == entry["when"]
|
||||||
|
|
||||||
if name:
|
if name:
|
||||||
assert name == entry["name"]
|
assert name == entry["name"]
|
||||||
@ -1639,3 +1640,8 @@ class MockLazyEventPartialState(ha.Event):
|
|||||||
def context_user_id(self):
|
def context_user_id(self):
|
||||||
"""Context user id of event."""
|
"""Context user id of event."""
|
||||||
return self.context.user_id
|
return self.context.user_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def time_fired_isoformat(self):
|
||||||
|
"""Time event was fired in utc isoformat."""
|
||||||
|
return process_timestamp_to_utc_isoformat(self.time_fired)
|
||||||
|
@ -3,10 +3,18 @@ from datetime import datetime
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
import pytz
|
||||||
from sqlalchemy import create_engine
|
from sqlalchemy import create_engine
|
||||||
from sqlalchemy.orm import scoped_session, sessionmaker
|
from sqlalchemy.orm import scoped_session, sessionmaker
|
||||||
|
|
||||||
from homeassistant.components.recorder.models import Base, Events, RecorderRuns, States
|
from homeassistant.components.recorder.models import (
|
||||||
|
Base,
|
||||||
|
Events,
|
||||||
|
RecorderRuns,
|
||||||
|
States,
|
||||||
|
process_timestamp,
|
||||||
|
process_timestamp_to_utc_isoformat,
|
||||||
|
)
|
||||||
from homeassistant.const import EVENT_STATE_CHANGED
|
from homeassistant.const import EVENT_STATE_CHANGED
|
||||||
import homeassistant.core as ha
|
import homeassistant.core as ha
|
||||||
from homeassistant.exceptions import InvalidEntityFormatError
|
from homeassistant.exceptions import InvalidEntityFormatError
|
||||||
@ -165,3 +173,68 @@ def test_states_from_native_invalid_entity_id():
|
|||||||
|
|
||||||
state = state.to_native(validate_entity_id=False)
|
state = state.to_native(validate_entity_id=False)
|
||||||
assert state.entity_id == "test.invalid__id"
|
assert state.entity_id == "test.invalid__id"
|
||||||
|
|
||||||
|
|
||||||
|
async def test_process_timestamp():
|
||||||
|
"""Test processing time stamp to UTC."""
|
||||||
|
datetime_with_tzinfo = datetime(2016, 7, 9, 11, 0, 0, tzinfo=dt.UTC)
|
||||||
|
datetime_without_tzinfo = datetime(2016, 7, 9, 11, 0, 0)
|
||||||
|
est = pytz.timezone("US/Eastern")
|
||||||
|
datetime_est_timezone = datetime(2016, 7, 9, 11, 0, 0, tzinfo=est)
|
||||||
|
nst = pytz.timezone("Canada/Newfoundland")
|
||||||
|
datetime_nst_timezone = datetime(2016, 7, 9, 11, 0, 0, tzinfo=nst)
|
||||||
|
hst = pytz.timezone("US/Hawaii")
|
||||||
|
datetime_hst_timezone = datetime(2016, 7, 9, 11, 0, 0, tzinfo=hst)
|
||||||
|
|
||||||
|
assert process_timestamp(datetime_with_tzinfo) == datetime(
|
||||||
|
2016, 7, 9, 11, 0, 0, tzinfo=dt.UTC
|
||||||
|
)
|
||||||
|
assert process_timestamp(datetime_without_tzinfo) == datetime(
|
||||||
|
2016, 7, 9, 11, 0, 0, tzinfo=dt.UTC
|
||||||
|
)
|
||||||
|
assert process_timestamp(datetime_est_timezone) == datetime(
|
||||||
|
2016, 7, 9, 15, 56, tzinfo=dt.UTC
|
||||||
|
)
|
||||||
|
assert process_timestamp(datetime_nst_timezone) == datetime(
|
||||||
|
2016, 7, 9, 14, 31, tzinfo=dt.UTC
|
||||||
|
)
|
||||||
|
assert process_timestamp(datetime_hst_timezone) == datetime(
|
||||||
|
2016, 7, 9, 21, 31, tzinfo=dt.UTC
|
||||||
|
)
|
||||||
|
assert process_timestamp(None) is None
|
||||||
|
|
||||||
|
|
||||||
|
async def test_process_timestamp_to_utc_isoformat():
|
||||||
|
"""Test processing time stamp to UTC isoformat."""
|
||||||
|
datetime_with_tzinfo = datetime(2016, 7, 9, 11, 0, 0, tzinfo=dt.UTC)
|
||||||
|
datetime_without_tzinfo = datetime(2016, 7, 9, 11, 0, 0)
|
||||||
|
est = pytz.timezone("US/Eastern")
|
||||||
|
datetime_est_timezone = datetime(2016, 7, 9, 11, 0, 0, tzinfo=est)
|
||||||
|
est = pytz.timezone("US/Eastern")
|
||||||
|
datetime_est_timezone = datetime(2016, 7, 9, 11, 0, 0, tzinfo=est)
|
||||||
|
nst = pytz.timezone("Canada/Newfoundland")
|
||||||
|
datetime_nst_timezone = datetime(2016, 7, 9, 11, 0, 0, tzinfo=nst)
|
||||||
|
hst = pytz.timezone("US/Hawaii")
|
||||||
|
datetime_hst_timezone = datetime(2016, 7, 9, 11, 0, 0, tzinfo=hst)
|
||||||
|
|
||||||
|
assert (
|
||||||
|
process_timestamp_to_utc_isoformat(datetime_with_tzinfo)
|
||||||
|
== "2016-07-09T11:00:00+00:00"
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
process_timestamp_to_utc_isoformat(datetime_without_tzinfo)
|
||||||
|
== "2016-07-09T11:00:00+00:00"
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
process_timestamp_to_utc_isoformat(datetime_est_timezone)
|
||||||
|
== "2016-07-09T15:56:00+00:00"
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
process_timestamp_to_utc_isoformat(datetime_nst_timezone)
|
||||||
|
== "2016-07-09T14:31:00+00:00"
|
||||||
|
)
|
||||||
|
assert (
|
||||||
|
process_timestamp_to_utc_isoformat(datetime_hst_timezone)
|
||||||
|
== "2016-07-09T21:31:00+00:00"
|
||||||
|
)
|
||||||
|
assert process_timestamp_to_utc_isoformat(None) is None
|
||||||
|
Loading…
x
Reference in New Issue
Block a user