From e2fa1d92fa631f808cac4ad6537b0188c84f1a9b Mon Sep 17 00:00:00 2001 From: Igor Leturia Date: Fri, 17 Jun 2022 07:50:03 +0100 Subject: [PATCH 1/3] port/MycroftAI/lingua-franca/pull/232 Improve nice_relative_time tests - Add more variations - Remove import from mycroft-core - Remove reference to local time to ensure deterministic outcome Ensure relative datetimes are compared in the same timezone. All comparisons are made in UTC to prevent issues of the two datetimes passed in having different timezone info. Remove unnecessary duplicate function The nice_relative_time function is already defined in the top level format.py hence it isn't needed in format_en.py. This convention may change in the future. Added general nice_relative_time_en Added test for function nice_relative_time Added function nice_relative_time (used in skill-alarm but not localized) --- lingua_franca/format.py | 57 +++++++++++++++++++++++++++++++- lingua_franca/lang/format_eu.py | 4 +++ test/unittests/test_format_en.py | 37 +++++++++++++++++++++ 3 files changed, 97 insertions(+), 1 deletion(-) diff --git a/lingua_franca/format.py b/lingua_franca/format.py index a7137ebc..30a6ca26 100755 --- a/lingua_franca/format.py +++ b/lingua_franca/format.py @@ -29,6 +29,7 @@ is_supported_full_lang, _raise_unsupported_language, \ UnsupportedLanguageError, NoneLangWarning, InvalidLangWarning, \ FunctionNotLocalizedError, resolve_resource_file, FunctionNotLocalizedError +from lingua_franca.time import now_local, to_local _REGISTERED_FUNCTIONS = ("nice_number", @@ -36,7 +37,8 @@ "pronounce_number", "pronounce_lang", "nice_response", - "nice_duration") + "nice_duration", + "nice_relative_time") populate_localized_function_dict("format", langs=get_active_langs()) @@ -578,3 +580,56 @@ def nice_response(text, lang=''): assertEqual(nice_response_de("10 ^ 2"), "10 hoch 2") """ + +@localized_function(run_own_code_on=[FunctionNotLocalizedError]) +def nice_relative_time(when, relative_to=None, lang=None): + """Create a relative phrase to roughly describe the period between two + datetimes. + + Examples are "25 seconds", "tomorrow", "7 days". + + Note: The reported period is currently limited to a number of days. Longer + periods such as multiple weeks or months will be reported in days. + + Args: + when (datetime): Local timezone + relative_to (datetime): Baseline for relative time, default is now() + lang (str, optional): Defaults to "en-us". + Returns: + str: Relative description of the given time + """ + if relative_to: + now = relative_to + else: + now = now_local() + delta = to_local(when) - now + + if delta.total_seconds() < 1: + return "now" + + if delta.total_seconds() < 90: + if delta.total_seconds() == 1: + return "one second" + else: + return "{} seconds".format(int(delta.total_seconds())) + + minutes = int((delta.total_seconds() + 30) // 60) # +30 to round minutes + if minutes < 90: + if minutes == 1: + return "one minute" + else: + return "{} minutes".format(minutes) + + hours = int((minutes + 30) // 60) # +30 to round hours + if hours < 36: + if hours == 1: + return "one hour" + else: + return "{} hours".format(hours) + + # TODO: "2 weeks", "3 months", "4 years", etc + days = int((hours + 12) // 24) # +12 to round days + if days == 1: + return "1 day" + else: + return "{} days".format(days) diff --git a/lingua_franca/lang/format_eu.py b/lingua_franca/lang/format_eu.py index c1ea0c31..50e715c9 100644 --- a/lingua_franca/lang/format_eu.py +++ b/lingua_franca/lang/format_eu.py @@ -327,11 +327,15 @@ def nice_time_eu(dt, speech=True, use_24hour=False, use_ampm=False): # hemen dago tranpa # return str(dt.hour) + ":" + str(dt.minute) + def nice_relative_time_eu(when, relative_to=None, lang=None): """Create a relative phrase to roughly describe a datetime Examples are "25 seconds", "tomorrow", "7 days". + Note: The reported period is currently limited to a number of days. Longer + periods such as multiple weeks or months will be reported in days. + Args: when (datetime): Local timezone relative_to (datetime): Baseline for relative time, default is now() diff --git a/test/unittests/test_format_en.py b/test/unittests/test_format_en.py index fae8ff6d..fd117734 100644 --- a/test/unittests/test_format_en.py +++ b/test/unittests/test_format_en.py @@ -37,6 +37,7 @@ from lingua_franca.format import date_time_format from lingua_franca.format import join_list from lingua_franca.format import pronounce_lang +from lingua_franca.format import nice_relative_time from lingua_franca.time import default_timezone, set_default_tz, now_local, \ to_local @@ -534,6 +535,42 @@ def test_join(self): self.assertEqual(join_list([1, "b", 3, "d"], "or"), "1, b, 3 or d") +class TestNiceRelativeTime(unittest.TestCase): + def test_format_nice_relative_time(self): + base_datetime = datetime.datetime(2017, 1, 31, 13, 22, 3, + tzinfo=default_timezone()) + two_hours_from_base = base_datetime + datetime.timedelta(hours=2) + self.assertEqual( + nice_relative_time(when=two_hours_from_base, relative_to=base_datetime), + "2 hours" + ) + twoish_hours_from_base = base_datetime + datetime.timedelta(hours=2, minutes=27) + self.assertEqual( + nice_relative_time(when=twoish_hours_from_base, relative_to=base_datetime), + "2 hours" + ) + seconds_from_base = base_datetime + datetime.timedelta(seconds=47) + self.assertEqual( + nice_relative_time(when=seconds_from_base, relative_to=base_datetime), + "47 seconds" + ) + three_days_from_base = base_datetime + datetime.timedelta(days=3) + self.assertEqual( + nice_relative_time(when=three_days_from_base, relative_to=base_datetime), + "3 days" + ) + almost_four_days_from_base = base_datetime + datetime.timedelta(days=3, hours=20) + self.assertEqual( + nice_relative_time(when=almost_four_days_from_base, relative_to=base_datetime), + "4 days" + ) + long_time_from_base = base_datetime + datetime.timedelta(days=957, hours=2, seconds=12) + self.assertEqual( + nice_relative_time(when=long_time_from_base, relative_to=base_datetime), + "957 days" + ) + + class TestLangcode(unittest.TestCase): def test_format_lang_code(self): self.assertEqual(pronounce_lang(lang_code="en"), "English") From a38a7ba69f4db4615fd182d65054e28471fedaf9 Mon Sep 17 00:00:00 2001 From: JarbasAI <33701864+JarbasAl@users.noreply.github.com> Date: Wed, 17 Jul 2024 14:19:55 +0100 Subject: [PATCH 2/3] Update lingua_franca/format.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- lingua_franca/format.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lingua_franca/format.py b/lingua_franca/format.py index 30a6ca26..7aa3b11b 100755 --- a/lingua_franca/format.py +++ b/lingua_franca/format.py @@ -611,7 +611,7 @@ def nice_relative_time(when, relative_to=None, lang=None): if delta.total_seconds() == 1: return "one second" else: - return "{} seconds".format(int(delta.total_seconds())) + return f"{int(delta.total_seconds())} seconds" minutes = int((delta.total_seconds() + 30) // 60) # +30 to round minutes if minutes < 90: From b6a8a8c561c7836ea2088dd39906f6f9a7dd41e9 Mon Sep 17 00:00:00 2001 From: JarbasAI <33701864+JarbasAl@users.noreply.github.com> Date: Wed, 17 Jul 2024 14:20:18 +0100 Subject: [PATCH 3/3] Update lingua_franca/format.py Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- lingua_franca/format.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lingua_franca/format.py b/lingua_franca/format.py index 7aa3b11b..576ce34a 100755 --- a/lingua_franca/format.py +++ b/lingua_franca/format.py @@ -598,10 +598,7 @@ def nice_relative_time(when, relative_to=None, lang=None): Returns: str: Relative description of the given time """ - if relative_to: - now = relative_to - else: - now = now_local() + now = relative_to if relative_to else now_local() delta = to_local(when) - now if delta.total_seconds() < 1: