diff --git a/__init__.py b/__init__.py index 6c1fa6a..b937a09 100644 --- a/__init__.py +++ b/__init__.py @@ -415,11 +415,11 @@ def handle_create_reminder(self, message: Message): connector="and", sep=",", lang=self.lang)} end = [a.until for a in overlapping if a.until] if end: - dialog_data["begin"] = nice_time(min([a.expiration for a in overlapping])) - dialog_data["end"] = nice_time(max(end)) + dialog_data["begin"] = nice_time(min([a.expiration for a in overlapping]), lang=self.lang) + dialog_data["end"] = nice_time(max(end), lang=self.lang) dialog = "alert_overlapping_duration_ask" else: - dialog_data["begin"] = join_word_list([nice_time(a.expiration) for a in overlapping], + dialog_data["begin"] = join_word_list([nice_time(a.expiration, lang=self.lang) for a in overlapping], connector="and", sep=",", lang=self.lang) dialog = "alert_overlapping_ask" @@ -1119,7 +1119,7 @@ def confirm_alert(self, alert: Alert, message: Message, # This is patching LF type annotation bug # noinspection PyTypeChecker spoken_alert_time = \ - nice_time(alert.expiration, self.lang, + nice_time(alert.expiration, lang=self.lang, use_24hour=self.use_24hour, use_ampm=not self.use_24hour) # Schedule alert expirations @@ -1260,7 +1260,8 @@ def converse(self, message: Message): for alert in active: self._snooze_alert(alert, snooze_duration) self.speak_dialog("confirm_snooze_alert", - {"duration": nice_duration(round(duration.total_seconds()))}) + {"duration": nice_duration(round(duration.total_seconds()), + lang=self.lang)}) return True return False @@ -1380,7 +1381,7 @@ def _pick_one_by_time(self, alerts: List[Alert], dialog: str = ""): alerts.sort(key=lambda x: x.expiration) spoken_list = [ f"{pronounce_number(i + 1)}. \ - {nice_date_time(alert.expiration, use_24hour=self.use_24hour, use_ampm=not self.use_24hour)}" + {nice_date_time(alert.expiration, use_24hour=self.use_24hour, use_ampm=not self.use_24hour, lang=self.lang)}" for i, alert in enumerate(alerts) ] self.speak(join_word_list(spoken_list, connector="and", sep=",", lang=self.lang), wait=True) @@ -1537,7 +1538,7 @@ def _display_list(self, alerts: List[Alert], header: str = "Todo"): "main": alert.alert_name, "secondary": "" if alert.expiration is None - else datetime_display(alert.expiration, alert.until), + else datetime_display(alert.expiration, alert.until, alert.lang), } for alert in alerts ] @@ -1689,8 +1690,8 @@ def _event_snooze_alarm(self, message): self._snooze_alert(alert) self.speak_dialog("confirm_snooze_alert", {"name": alert.alert_name, - "duration": nice_duration( - self.snooze_duration)}, wait=True) + "duration": nice_duration(self.snooze_duration, lang=self.lang)}, + wait=True) def _gui_dismiss_notification(self, message): if not message.data.get('alert'): @@ -1739,7 +1740,7 @@ def _alert_prenotification(self, alert: Alert): "alert_prenotification", { "reminder": alert.alert_name, - "time_until": nice_duration(alert.time_to_expiration), + "time_until": nice_duration(alert.time_to_expiration, lang=self.lang), }, ) time.sleep(min(20, self.alert_timeout_seconds)) diff --git a/setup.py b/setup.py index 1ed3d5b..417a9bc 100644 --- a/setup.py +++ b/setup.py @@ -90,7 +90,7 @@ def get_requirements(requirements_filename: str): def find_resource_files(): - resource_base_dirs = ("locale", "gui", "vocab", "dialog", "regex", "res") + resource_base_dirs = ("locale", "gui", "res") package_data = ["skill.json"] for res in resource_base_dirs: if path.isdir(path.join(BASE_PATH, res)): diff --git a/test/test_skill.py b/test/test_skill.py index c5cb495..7db9a95 100644 --- a/test/test_skill.py +++ b/test/test_skill.py @@ -451,8 +451,8 @@ def test_handle_create_event(self): overlapping_reminder = _get_message_from_file( "reminder_event_length_at_time.json" ) - begin = nice_time(self.valid_event.expiration) - end = nice_time(self.valid_event.until) + begin = nice_time(self.valid_event.expiration, lang="en-us") + end = nice_time(self.valid_event.until, lang="en-us") self.skill.handle_create_event(overlapping_reminder) self.skill.ask_yesno.assert_called_once() self.skill.ask_yesno.assert_called_with( @@ -469,7 +469,7 @@ def test_handle_create_event(self): overlapping_reminder2 = _get_message_from_file( "reminder_event_length_at_time2.json" ) - begin = nice_time(self.valid_event2.expiration) + begin = nice_time(self.valid_event2.expiration, lang="en-us") self.skill.handle_create_event(overlapping_reminder2) self.skill.ask_yesno.assert_called_with( "alert_overlapping_ask", {"event": "valid event 2", @@ -718,7 +718,7 @@ def test_handle_event_timeframe_check(self): # check in a time range (note: 1 minute gap 11:59-00:00) check_message.data["utterance"] = \ f"are there any events between {day} 00:00 am and 11:59 pm" - begin = nice_time(self.valid_event.expiration, use_ampm=True) + begin = nice_time(self.valid_event.expiration, use_ampm=True, lang="en-us") self.skill.handle_event_timeframe_check(check_message) self.assertEqual(self.skill.speak_dialog.call_count, 2) @@ -793,9 +793,8 @@ def test_handle_next_alert(self): { "kind": "alarm", "name": "", - "begin": nice_date_time(self.valid_alarm_1.expiration, use_ampm=True), - "remaining": spoken_duration(self.valid_alarm_1.expiration, - lang="en-us") + "begin": nice_date_time(self.valid_alarm_1.expiration, use_ampm=True, lang="en-us"), + "remaining": spoken_duration(self.valid_alarm_1.expiration, lang="en-us") }, wait=True ) @@ -806,9 +805,8 @@ def test_handle_next_alert(self): { "kind": "timer", "name": "oven", - "begin": nice_time(self.valid_timer.expiration, use_ampm=True), - "remaining": spoken_duration(self.valid_timer.expiration, - lang="en-us") + "begin": nice_time(self.valid_timer.expiration, use_ampm=True, lang="en-us"), + "remaining": spoken_duration(self.valid_timer.expiration, lang="en-us") }, wait=True ) @@ -820,10 +818,9 @@ def test_handle_next_alert(self): { "kind": "reminder", "name": self.valid_reminder.alert_name, - "begin": nice_date_time(self.valid_reminder.expiration, use_ampm=True), - "end": nice_date_time(self.valid_reminder.until, use_ampm=True), - "remaining": spoken_duration(self.valid_reminder.expiration, - lang="en-us") + "begin": nice_date_time(self.valid_reminder.expiration, use_ampm=True, lang="en-us"), + "end": nice_date_time(self.valid_reminder.until, use_ampm=True, lang="en-us"), + "remaining": spoken_duration(self.valid_reminder.expiration, lang="en-us") }, wait=True ) @@ -835,9 +832,8 @@ def test_handle_next_alert(self): { "kind": "timer", "name": "oven", - "begin": nice_time(self.valid_timer.expiration, use_ampm=True), - "remaining": spoken_duration(self.valid_timer.expiration, - lang="en-us") + "begin": nice_time(self.valid_timer.expiration, use_ampm=True, lang="en-us"), + "remaining": spoken_duration(self.valid_timer.expiration, lang="en-us") }, wait=True ) @@ -1483,10 +1479,10 @@ def test_get_alert_dialog_data(self, mock_spoken_type, mock_translate): "name": "appointment", "kind": "translated", "begin": nice_time( - today_alert.expiration, use_24hour=False, use_ampm=True + today_alert.expiration, use_24hour=False, use_ampm=True, lang="en-us" ), "end": nice_time( - today_alert.until, use_24hour=False, use_ampm=True + today_alert.until, use_24hour=False, use_ampm=True, lang="en-us" ), "remaining": "one minute", }, @@ -1505,10 +1501,9 @@ def test_get_alert_dialog_data(self, mock_spoken_type, mock_translate): "name": "wakeup", "kind": "translated", "begin": nice_date_time( - one_time.expiration, use_24hour=False, use_ampm=True + one_time.expiration, use_24hour=False, use_ampm=True, lang="en-us" ), - "remaining": spoken_duration(one_time.expiration, - lang="en-us") + "remaining": spoken_duration(one_time.expiration, lang="en-us") }, ) @@ -1529,10 +1524,9 @@ def test_get_alert_dialog_data(self, mock_spoken_type, mock_translate): "kind": "translated", "repeat": "translated", "begin": nice_time( - weekend.expiration, use_24hour=False, use_ampm=True + weekend.expiration, use_24hour=False, use_ampm=True, lang="en-us" ), - "remaining": spoken_duration(weekend.expiration, - lang="en-us") + "remaining": spoken_duration(weekend.expiration, lang="en-us") }, ) @@ -1559,10 +1553,9 @@ def test_get_alert_dialog_data(self, mock_spoken_type, mock_translate): "kind": "translated", "repeat": "translated", "begin": nice_time( - weekday.expiration, use_24hour=False, use_ampm=True + weekday.expiration, use_24hour=False, use_ampm=True, lang="en-us" ), - "remaining": spoken_duration(weekday.expiration, - lang="en-us") + "remaining": spoken_duration(weekday.expiration, lang="en-us") }, ) @@ -1591,10 +1584,9 @@ def test_get_alert_dialog_data(self, mock_spoken_type, mock_translate): "repeat": "translated", "kind": "translated", "begin": nice_time( - daily.expiration, use_24hour=False, use_ampm=True + daily.expiration, use_24hour=False, use_ampm=True, lang="en-us" ), - "remaining": spoken_duration(daily.expiration, - lang="en-us") + "remaining": spoken_duration(daily.expiration, lang="en-us") }, ) @@ -1615,10 +1607,9 @@ def test_get_alert_dialog_data(self, mock_spoken_type, mock_translate): "repeat": "translated", "kind": "translated", "begin": nice_time( - weekly.expiration, use_24hour=False, use_ampm=True + weekly.expiration, use_24hour=False, use_ampm=True, lang="en-us" ), - "remaining": spoken_duration(daily.expiration, - lang="en-us") + "remaining": spoken_duration(daily.expiration, lang="en-us") }, ) @@ -1635,12 +1626,11 @@ def test_get_alert_dialog_data(self, mock_spoken_type, mock_translate): { "name": "take pill", "kind": "translated", - "repeat": nice_duration(dt.timedelta(hours=8).total_seconds()), + "repeat": nice_duration(dt.timedelta(hours=8).total_seconds(), lang="en-us"), "begin": nice_time( - eight_hour.expiration, use_24hour=False, use_ampm=True + eight_hour.expiration, use_24hour=False, use_ampm=True, lang="en-us" ), - "remaining": spoken_duration(daily.expiration, - lang="en-us") + "remaining": spoken_duration(daily.expiration, lang="en-us") }, ) @@ -1661,7 +1651,7 @@ def test_get_alert_dialog_data(self, mock_spoken_type, mock_translate): "name": "", "kind": "timer", "begin": nice_time( - two_minute.expiration, use_24hour=False, use_ampm=True + two_minute.expiration, use_24hour=False, use_ampm=True, lang="en-us" ), "remaining": "two minutes" }, diff --git a/util/alert.py b/util/alert.py index 99b12aa..6416586 100644 --- a/util/alert.py +++ b/util/alert.py @@ -36,9 +36,10 @@ from dateutil.relativedelta import relativedelta from json_database.utils import merge_dict from ovos_utils.log import LOG -from ovos_config.locale import get_default_tz +from ovos_config.locale import get_default_tz, get_default_lang from ovos_config import Configuration + from ovos_skill_alerts.util import AlertType, DAVType, AlertPriority, Weekdays from ovos_skill_alerts.util.dav_utils import process_ical_event, process_ical_todo @@ -105,7 +106,7 @@ def lang(self) -> str: """ Returns the lang associated with this alert """ - return self._data.get("lang") + return self._data.get("lang") or get_default_lang() @property def created(self) -> dt.datetime: diff --git a/util/locale.py b/util/locale.py index b698edb..5c9fffd 100644 --- a/util/locale.py +++ b/util/locale.py @@ -20,24 +20,28 @@ def datetime_display(begin: dt.datetime, - end: Optional[dt.datetime] = None) -> str: + end: Optional[dt.datetime] = None, + lang: str = None) -> str: + lang = lang or get_default_lang() use_24h = use_24h_format() date_format = get_date_format() display = date_display(begin, date_format) + " " + \ - time_display(begin, use_24h) + time_display(begin, use_24h, lang=lang) if end and end.date() != begin.date(): display += " - " + date_display(end, date_format) + " " + \ - time_display(end, use_24h) + time_display(end, use_24h, lang=lang) elif end: - display += " - " + time_display(end, use_24h) + display += " - " + time_display(end, use_24h, lang=lang) return display def time_display(dt_obj: dt.datetime, - use_24h: bool = True) -> str: - return nice_time(dt_obj, + use_24h: bool = True, + lang: str = None) -> str: + lang = lang or get_default_lang() + return nice_time(dt_obj, lang=lang, speech=False, use_24hour=use_24h, use_ampm=not use_24h) @@ -51,6 +55,8 @@ def date_display(dt_obj: dt.datetime, display = dt_obj.strftime("%-d/%-m/%Y") elif date_format == "YMD": display = dt_obj.strftime("%Y/%-m/%-d") + else: + raise ValueError(f"Invalid date format: {date_format}") return display @@ -134,7 +140,6 @@ def spoken_duration(alert_time: Union[dt.timedelta, dt.datetime], :param lang: Language to format response in :return: speakable duration string """ - load_language(lang or get_default_lang()) if isinstance(alert_time, dt.datetime): anchor_time = anchor_time or \ dt.datetime.now(alert_time.tzinfo).replace(microsecond=0) @@ -156,8 +161,7 @@ def spoken_duration(alert_time: Union[dt.timedelta, dt.datetime], else: _seconds = remaining_time.total_seconds() - return nice_duration(int(_seconds), - lang=lang) + return nice_duration(int(_seconds), lang=lang or get_default_lang()) def get_abbreviation(wd: Weekdays) -> str: @@ -240,8 +244,7 @@ def get_alert_dialog_data(alert: Alert, :param lang: User language to be spoken :returns: dict dialog_data to pass to `speak_dialog` """ - lang = lang or get_default_lang() - load_language(lang) + lang = lang or alert.lang use_24hour = use_24h_format() expired_time = alert.data["next_expiration_time"] @@ -255,11 +258,11 @@ def get_alert_dialog_data(alert: Alert, if anchor_date.date() != expired_time.date() and \ not alert.has_repeat: if alert.is_all_day: - spoken_time = f"{nice_date(expired_time, lang)}, " + spoken_time = f"{nice_date(expired_time, lang=lang)}, " spoken_time += translate("whole_day", lang) else: # noinspection PyTypeChecker - spoken_time = nice_date_time(expired_time, lang, + spoken_time = nice_date_time(expired_time, lang=lang, use_24hour=use_24hour, use_ampm=not use_24hour) else: @@ -267,7 +270,7 @@ def get_alert_dialog_data(alert: Alert, spoken_time = translate("whole_day", lang) else: # noinspection PyTypeChecker - spoken_time = nice_time(expired_time, lang, + spoken_time = nice_time(expired_time, lang=lang, use_24hour=use_24hour, use_ampm=not use_24hour) @@ -282,12 +285,12 @@ def get_alert_dialog_data(alert: Alert, # add event end if alert.until and not alert.is_all_day: if anchor_date.date() != alert.until.date(): - spoken_time = nice_date_time(alert.until, lang, + spoken_time = nice_date_time(alert.until, lang=lang, use_24hour=use_24hour, use_ampm=not use_24hour) else: # noinspection PyTypeChecker - spoken_time = nice_time(alert.until, lang, + spoken_time = nice_time(alert.until, lang=lang, use_24hour=use_24hour, use_ampm=not use_24hour) data["end"] = spoken_time @@ -305,10 +308,10 @@ def get_alert_dialog_data(alert: Alert, connector="and", sep=",", lang=lang) elif alert.repeat_frequency: data["repeat"] = nice_duration( - alert.repeat_frequency.total_seconds()) + alert.repeat_frequency.total_seconds(), lang=lang) if alert.prenotification: - data["prenotification"] = nice_time(alert.prenotification) + data["prenotification"] = nice_time(alert.prenotification, lang=lang) return data diff --git a/util/parse_utils.py b/util/parse_utils.py index 484851e..0b6edc3 100644 --- a/util/parse_utils.py +++ b/util/parse_utils.py @@ -64,14 +64,14 @@ def __init__(self, chunks: list, message: Message = None): def lang(self): return self.message.data.get("lang") or get_default_lang() - def unmatched(self, original = False): + def unmatched(self, original=False): if original: list_ = self.original else: list_ = self return [chunk for chunk in list_ if not (self.is_matched(chunk) or - chunk in get_words_list("noise_words.voc", self.lang))] + chunk in get_words_list("noise_words.voc", self.lang))] def is_matched(self, chunk): return any([tag["match"] == chunk @@ -80,9 +80,9 @@ def is_matched(self, chunk): def strip_time(self): if self.time_stripped: return - + time_data = [] - lang = self.message.data.get("lang") + lang = self.message.data.get("lang") or get_default_lang() for i, token in enumerate(self): if self.is_matched(token): continue @@ -102,9 +102,9 @@ def strip_time(self): if time_: time_data.append(time_) self[i] = remainder - + self.extracted_time = time_data[0] if time_data else None - + def clear(self): self[:] = self.original self.time_stripped = False @@ -177,7 +177,7 @@ def get_default_alert_name(alert_time: Union[dt.date, dt.datetime, dt.timedelta] return spoken_alert_type(alert_type, lang) timezone = timezone or alert_time.tzinfo if \ - isinstance(alert_time, dt.datetime) else get_default_tz() + isinstance(alert_time, dt.datetime) else get_default_tz() now_time = now_time or dt.datetime.now(tz=timezone) lang = lang or get_default_lang() load_language(lang) @@ -185,12 +185,12 @@ def get_default_alert_name(alert_time: Union[dt.date, dt.datetime, dt.timedelta] time_str = spoken_duration(alert_time, now_time, lang) # TODO ordinalize (LF) elif alert_time.__class__ == dt.date: - time_str = nice_day(alert_time, get_date_format(), lang=lang) + time_str = nice_day(alert_time, date_format=get_date_format(), lang=lang) else: use_24hr = use_24h_format() if isinstance(alert_time, dt.timedelta): alert_time += now_time - time_str = nice_time(alert_time, lang, False, use_24hr, not use_24hr) + time_str = nice_time(alert_time, lang=lang, speech=False, use_24hour=use_24hr, use_ampm=not use_24hr) return f"{time_str} {spoken_alert_type(alert_type, lang)}" @@ -245,7 +245,7 @@ def build_alert_from_intent(message: Message) -> Optional[Alert]: until = parse_end_condition_from_message(message, tokens, timezone, alert_time or anchor_time) data["audio_file"] = parse_audio_file_from_message(message, tokens) - if isinstance(until, (dt.timedelta, relativedelta,)): + if isinstance(until, (dt.timedelta, relativedelta,)): until = (alert_time or anchor_time) + until if alert_type == AlertType.TIMER and alert_time is None: @@ -267,11 +267,11 @@ def build_alert_from_intent(message: Message) -> Optional[Alert]: data["repeat_days"] = repeat_days data["alert_name"] = parse_alert_name_from_message(message, tokens) \ - or get_default_alert_name(alert_time, - get_alert_type(message), - timezone, - now_time=anchor_time, - lang=lang) + or get_default_alert_name(alert_time, + get_alert_type(message), + timezone, + now_time=anchor_time, + lang=lang) data["alert_type"] = alert_type if alert_type == AlertType.TODO: data["dav_type"] = DAVType.VTODO @@ -301,7 +301,7 @@ def parse_repeat_from_message(message: Message, elif message.data.get("repeat"): tokens = tokens or tokenize_utterance(message) repeat_index = tokens.index(message.data["repeat"]) + 1 - if repeat_index > len(tokens)-1: + if repeat_index > len(tokens) - 1: return [] repeat_clause = tokens.pop(repeat_index) @@ -330,11 +330,10 @@ def parse_repeat_from_message(message: Message, # Parse repeat interval if not repeat_days: - extracted_duration = extract_duration(repeat_clause, lang) + extracted_duration = extract_duration(repeat_clause, lang=lang) if extracted_duration and not extracted_duration[0]: # Replace "the next week" with "1 week", etc. - extracted_duration = extract_duration( - f"1 {extracted_duration[1]}", lang) + extracted_duration = extract_duration(f"1 {extracted_duration[1]}", lang=lang) if extracted_duration and extracted_duration[0]: duration, remainder = extracted_duration if remainder and remainder.strip(): @@ -372,27 +371,26 @@ def parse_end_condition_from_message(message: Message, anchor_date = anchor_time or dt.datetime.now(timezone) if message.data.get("until"): idx = tokens.index(message.data["until"]) + 1 - if idx > len(tokens)-1: + if idx > len(tokens) - 1: return None end_clause = tokens.pop(idx) - extracted_duration = extract_duration(end_clause, lang) + extracted_duration = extract_duration(end_clause, lang=lang) # extract duration first because of overlaps if extracted_duration and not extracted_duration[0]: # Replace "the next week" with "1 week", etc. - extracted_duration = extract_duration( - f"1 {extracted_duration[1]}", lang) + extracted_duration = extract_duration(f"1 {extracted_duration[1]}", lang=lang) if extracted_duration and extracted_duration[0]: end_time, remainder = extracted_duration else: - extracted_dt = extract_datetime(end_clause, anchor_date, lang) + extracted_dt = extract_datetime(end_clause, anchorDate=anchor_date, lang=lang) if extracted_dt is None: end_time = extracted_dt remainder = end_clause else: end_time, remainder = extracted_dt tokens.insert(idx, remainder) - + LOG.debug(f"Parsed end time from message: {end_time}") return end_time elif message.data.get("all_day"): @@ -466,7 +464,8 @@ def parse_timedelta_from_message(message: Message, alert_time = parse_alert_time_from_message(message, tokens, timezone, anchor_time) - return alert_time-anchor_time if alert_time else None + return alert_time - anchor_time if alert_time else None + # TODO: Toss - should be handled via OCP def parse_audio_file_from_message(message: Message, @@ -497,12 +496,12 @@ def parse_alert_priority_from_message(message: Message, :param tokens: optional tokens parsed from message by `tokenize_utterances` """ tokens = tokens or tokenize_utterance(message) - load_language(message.data.get("lang") or get_default_lang()) + lang = message.data.get("lang") or get_default_lang() priority = AlertPriority.AVERAGE.value if message.data.get("priority"): - num = extract_number(" ".join(tokens.unmatched())) - priority = num if num and num in range(1,11) else priority + num = extract_number(" ".join(tokens.unmatched()), lang=lang) + priority = num if num and num in range(1, 11) else priority return priority @@ -540,10 +539,10 @@ def parse_alert_name_from_message(message: Message, def parse_alert_name_and_time_from_message( - message: Message, - tokens: list = None, - timezone: dt.tzinfo = None, - anchor_time: dt.datetime = None + message: Message, + tokens: list = None, + timezone: dt.tzinfo = None, + anchor_time: dt.datetime = None ) -> Tuple[Optional[str], Optional[dt.datetime]]: """ Parse an alert name and time from a request @@ -583,10 +582,11 @@ def parse_alert_context_from_message(message: Message) -> dict: # TODO: no need for timezone in the signature def extract_dt_or_duration( - text: str, - anchor_time: dt.datetime = None, - timezone: dt.tzinfo = None, - add: bool = True + text: str, + anchor_time: dt.datetime = None, + timezone: dt.tzinfo = None, + add: bool = True, + lang=None ) -> Union[dt.datetime, dt.timedelta, None]: """ Helper to extract either a duration or datetime @@ -596,30 +596,30 @@ def extract_dt_or_duration( :param anchorDate: a datetime to which a timedelta is added to :param add: whether a duration should be added or substracted """ + lang = lang or get_default_lang() # timezone = timezone or get_default_tz() # if anchor_time: # timezone = anchor_time.tzinfo # else: # anchor_time = dt.datetime.now(timezone) - time_, remainder = extract_duration(text) + time_, remainder = extract_duration(text, lang=lang) if time_ is None: - #now = dt.datetime.now(timezone) - time_, remainder = extract_datetime(text, anchorDate=anchor_time) or (None, text) - if isinstance(time_, (dt.timedelta, relativedelta)): + # now = dt.datetime.now(timezone) + time_, remainder = extract_datetime(text, anchorDate=anchor_time, lang=lang) or (None, text) + if isinstance(time_, (dt.timedelta, relativedelta)): if anchor_time is not None: return anchor_time + time_ if add else anchor_time - time_, remainder else: return time_ if add else time_ * -1, remainder - + return time_, remainder def parse_relative_time_from_message(message: Message, tokens: Optional[Tokens] = None, timezone: Optional[dt.tzinfo] = None, - anchor_time: Optional[dt.datetime]= None): - + anchor_time: Optional[dt.datetime] = None): lang = message.data.get("lang") or get_default_lang() earlier = "earlier" in message.data load_language(lang) @@ -631,7 +631,7 @@ def parse_relative_time_from_message(message: Message, time_, remainder = extract_dt_or_duration(token, anchor_time, timezone, - not earlier) + not earlier, lang=lang) if time_: tokens[tokens.index(token)] = remainder return time_ @@ -660,34 +660,36 @@ def parse_timeframe_from_message(message: Message, # eg "today", "tomorrow", "3rd of october", "tuesday" if end is None and begin and begin.minute == 0 and begin.hour == 0: end = begin + dt.timedelta(days=1, seconds=-1) - - return begin, end + + return begin, end -def validate_dt_or_delta(response: str) -> Union[dt.datetime, dt.timedelta, str]: +def validate_dt_or_delta(response: str, lang: str = None) -> Union[dt.datetime, dt.timedelta, str]: """ Validates if the sentence contains a datetime or duration """ - - if voc_match(response, "no"): + lang = lang or get_default_lang() + if voc_match(response, "no", lang): return "no" else: - return extract_dt_or_duration(response)[0] + return extract_dt_or_duration(response, lang=lang)[0] -def validate_dt(response: str) -> Union[dt.datetime, str]: +def validate_dt(response: str, lang: str = None) -> Union[dt.datetime, str]: """ Validates if the sentence contains a datetime """ - if voc_match(response, "no"): + lang = lang or get_default_lang() + if voc_match(response, "no", lang): return "no" else: - dt_ = extract_datetime(response) + dt_ = extract_datetime(response, lang=lang) return dt_ if dt_ is None else dt_[0] -def validate_number(response: str) -> int: - num = extract_number(response) +def validate_number(response: str, lang: str = None) -> int: + lang = lang or get_default_lang() + num = extract_number(response, lang=lang) if num: return int(num) return False @@ -727,12 +729,13 @@ def fuzzy_match(test: str, against: str, confidence: int = None) -> Any: partial_ratio = fuzz.partial_ratio(test, against) token_sort_ratio = fuzz.token_sort_ratio(test, against) best = max(ratio, partial_ratio, token_sort_ratio) - #LOG.debug(f"Fuzzy Match value {best}") + # LOG.debug(f"Fuzzy Match value {best}") return best > confidence if confidence is not None else best + def fuzzy_match_alerts(test: List[Alert], against: str, confidence: int = None) \ - -> Optional[Alert]: + -> Optional[Alert]: """ Fuzzy matches a list of Alerts and returns the best match (alert names are tested) @@ -747,5 +750,5 @@ def fuzzy_match_alerts(test: List[Alert], against: str, confidence: int = None) if confidence and fuzzy_conf > confidence: result.append((alert, fuzzy_conf)) if result: - return sorted(result, key=lambda x:x[1])[-1][0] + return sorted(result, key=lambda x: x[1])[-1][0] return None diff --git a/util/ui_models.py b/util/ui_models.py index 5d0ac36..21dc489 100644 --- a/util/ui_models.py +++ b/util/ui_models.py @@ -59,13 +59,13 @@ def build_timer_data(alert: Alert) -> dict: if delta_seconds.total_seconds() < 0: percent_remaining = 0 human_delta = '-' + nice_duration(-1 * delta_seconds.total_seconds(), - speech=False) + speech=False, lang=alert.lang) else: total_time = (datetime.now(alert.timezone).timestamp() - start_time.timestamp()) + \ delta_seconds.total_seconds() percent_remaining = delta_seconds.total_seconds() / total_time - human_delta = nice_duration(delta_seconds.total_seconds(), speech=False) + human_delta = nice_duration(delta_seconds.total_seconds(), speech=False, lang=alert.lang) return { 'alertId': alert.ident, @@ -87,7 +87,7 @@ def build_alarm_data(alert: Alert) -> dict: use_24h = use_24h_format() alarm_time = nice_time( datetime.fromisoformat(alert.data["next_expiration_time"]), - speech=False, use_ampm=not use_24h, use_24hour=use_24h + speech=False, use_ampm=not use_24h, use_24hour=use_24h, lang=alert.lang ) if use_24h: alarm_time = alarm_time @@ -111,7 +111,7 @@ def build_alarm_data(alert: Alert) -> dict: "alarmExpired": alarm_expired, "alarmIndex": alarm_index, "alarmRepeat": alert.has_repeat, - "alarmRepeatStr" : alarm_repeat_str + "alarmRepeatStr": alarm_repeat_str } @@ -141,11 +141,11 @@ def get_sequences(d): repeat_str = ",".join(sequences) elif alert.repeat_frequency: repeat_str = nice_duration(alert.repeat_frequency.total_seconds(), - speech=False) + speech=False, lang=alert.lang) if alert.until: if repeat_str: repeat_str += "|" - repeat_str += f"{translate('until')} {datetime_display(alert.until.date())}" + repeat_str += f"{translate('until')} {datetime_display(alert.until.date(), lang=alert.lang)}" return repeat_str