From d5a1c52359f57364960c90dcf1ec4036ff0e4168 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Mon, 9 May 2016 23:31:02 -0700 Subject: [PATCH 1/5] Add a Jinja filter for relative time --- homeassistant/helpers/template.py | 8 ++++++++ requirements_all.txt | 1 + 2 files changed, 9 insertions(+) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 8e039432728..2d9563ade86 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -6,6 +6,8 @@ import logging import jinja2 from jinja2.sandbox import ImmutableSandboxedEnvironment +import humanize + from homeassistant.components import group from homeassistant.const import STATE_UNKNOWN, ATTR_LATITUDE, ATTR_LONGITUDE from homeassistant.core import State @@ -38,6 +40,11 @@ def render_with_possible_json_value(hass, template, value, return value if error_value is _SENTINEL else error_value +def relative_time(end_time): + """Return a relative (human readable) timestamp for the given time.""" + return humanize.naturaltime(dt_util.now() - end_time) + + def render(hass, template, variables=None, **kwargs): """Render given template.""" if variables is not None: @@ -57,6 +64,7 @@ def render(hass, template, variables=None, **kwargs): 'states': AllStates(hass), 'utcnow': utcnow, 'as_timestamp': dt_util.as_timestamp, + 'relative_time': relative_time }).render(kwargs).strip() except jinja2.TemplateError as err: raise TemplateError(err) diff --git a/requirements_all.txt b/requirements_all.txt index ddfc5a0802f..6fd11b160b2 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -6,6 +6,7 @@ pip>=7.0.0 vincenty==0.1.4 jinja2>=2.8 voluptuous==0.8.9 +humanize==0.5.1 # homeassistant.components.isy994 PyISY==1.0.5 From 16933abce95a434e3cab0bab10a30170758fc5d1 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Tue, 10 May 2016 00:04:53 -0700 Subject: [PATCH 2/5] Remove humanize and use a relative time thing that @balloob found on Github --- homeassistant/helpers/template.py | 4 +-- homeassistant/util/dt.py | 46 +++++++++++++++++++++++++++++++ requirements_all.txt | 1 - 3 files changed, 47 insertions(+), 4 deletions(-) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 2d9563ade86..a2a1856d3c2 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -6,8 +6,6 @@ import logging import jinja2 from jinja2.sandbox import ImmutableSandboxedEnvironment -import humanize - from homeassistant.components import group from homeassistant.const import STATE_UNKNOWN, ATTR_LATITUDE, ATTR_LONGITUDE from homeassistant.core import State @@ -42,7 +40,7 @@ def render_with_possible_json_value(hass, template, value, def relative_time(end_time): """Return a relative (human readable) timestamp for the given time.""" - return humanize.naturaltime(dt_util.now() - end_time) + return dt_util.get_age(end_time) def render(hass, template, variables=None, **kwargs): diff --git a/homeassistant/util/dt.py b/homeassistant/util/dt.py index 16e8dfebfd1..b06b70f2cdd 100644 --- a/homeassistant/util/dt.py +++ b/homeassistant/util/dt.py @@ -152,3 +152,49 @@ def parse_time(time_str): except ValueError: # ValueError if value cannot be converted to an int or not in range return None + + +# Found in this gist: https://gist.github.com/zhangsen/1199964 +def get_age(date): + """ + Take a datetime and return its "age" as a string. + + The age can be in second, minute, hour, day, month or year. Only the + biggest unit is considered, e.g. if it's 2 days and 3 hours, "2 days" will + be returned. + Make sure date is not in the future, or else it won't work. + """ + def formatn(number, unit): + """Add "unit" if it's plural.""" + if number == 1: + return "1 %s" % unit + elif number > 1: + return "%d %ss" % (number, unit) + + def q_n_r(first, second): + """Return quotient and remaining.""" + return first // second, first % second + + # pylint: disable=too-few-public-methods + class PrettyDelta: + """A class for relative times.""" + + def __init__(self, subDt): + delta = now() - subDt + self.day = delta.days + self.second = delta.seconds + + self.year, self.day = q_n_r(self.day, 365) + self.month, self.day = q_n_r(self.day, 30) + self.hour, self.second = q_n_r(self.second, 3600) + self.minute, self.second = q_n_r(self.second, 60) + + def format(self): + """Format a datetime to relative time string.""" + for period in ['year', 'month', 'day', 'hour', 'minute', 'second']: + number = getattr(self, period) + if number > 0: + return formatn(number, period) + return "0 second" + + return PrettyDelta(date).format() diff --git a/requirements_all.txt b/requirements_all.txt index 6fd11b160b2..ddfc5a0802f 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -6,7 +6,6 @@ pip>=7.0.0 vincenty==0.1.4 jinja2>=2.8 voluptuous==0.8.9 -humanize==0.5.1 # homeassistant.components.isy994 PyISY==1.0.5 From b75aa6ac08a5dbe76559d76c6b8ffbb734c31327 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Wed, 11 May 2016 22:29:55 -0700 Subject: [PATCH 3/5] Add get_age tests --- tests/util/test_dt.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/util/test_dt.py b/tests/util/test_dt.py index da5b56d42a9..1b51287384c 100644 --- a/tests/util/test_dt.py +++ b/tests/util/test_dt.py @@ -133,3 +133,8 @@ class TestDateUtil(unittest.TestCase): def test_parse_datetime_returns_none_for_incorrect_format(self): """Test parse_datetime returns None if incorrect format.""" self.assertIsNone(dt_util.parse_datetime("not a datetime string")) + + def test_get_age(self): + """Test get_age returns 5 minutes.""" + fiveminago = dt_util.now() - timedelta(minutes=5) + self.assertEqual(dt_util.get_age(fiveminago), "5 minutes") From fca4ec2b3e37ef9a182457d4f14b96230f35d266 Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Wed, 11 May 2016 22:37:37 -0700 Subject: [PATCH 4/5] simplify the relative_time function --- homeassistant/helpers/template.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index a2a1856d3c2..58dcd300500 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -37,12 +37,6 @@ def render_with_possible_json_value(hass, template, value, _LOGGER.error('Error parsing value: %s', ex) return value if error_value is _SENTINEL else error_value - -def relative_time(end_time): - """Return a relative (human readable) timestamp for the given time.""" - return dt_util.get_age(end_time) - - def render(hass, template, variables=None, **kwargs): """Render given template.""" if variables is not None: @@ -62,7 +56,7 @@ def render(hass, template, variables=None, **kwargs): 'states': AllStates(hass), 'utcnow': utcnow, 'as_timestamp': dt_util.as_timestamp, - 'relative_time': relative_time + 'relative_time': dt_util.get_age }).render(kwargs).strip() except jinja2.TemplateError as err: raise TemplateError(err) From 4d0b9f1e94d4d95f6ee511cb755b71c711ba460c Mon Sep 17 00:00:00 2001 From: Robbie Trencheny Date: Wed, 11 May 2016 22:44:44 -0700 Subject: [PATCH 5/5] Stupid blank lines --- homeassistant/helpers/template.py | 1 + 1 file changed, 1 insertion(+) diff --git a/homeassistant/helpers/template.py b/homeassistant/helpers/template.py index 58dcd300500..41de1782a1f 100644 --- a/homeassistant/helpers/template.py +++ b/homeassistant/helpers/template.py @@ -37,6 +37,7 @@ def render_with_possible_json_value(hass, template, value, _LOGGER.error('Error parsing value: %s', ex) return value if error_value is _SENTINEL else error_value + def render(hass, template, variables=None, **kwargs): """Render given template.""" if variables is not None: