Esempio n. 1
0
    def handle_read(self, message):
        data = {'list_name': message.data.get('list_name')}

        # If the user specified a list, read the items on that list
        if data['list_name']:
            # Check that the list exists
            if not self.db.list_exists(data['list_name']):
                self.speak_dialog('list.not.found', data)

            # Check that the list is not empty
            elif self.db.list_empty(data['list_name']):
                self.speak_dialog('no.items', data)

            else:
                data['items'] = join_list(self.db.read_items(
                    data['list_name']),
                                          self.translate('and'),
                                          lang=self.lang)
                self.speak_dialog('read.items', data)

        # Alternatively, simply read lists names
        else:
            # Check if there are lists at all
            if self.db.no_lists():
                self.speak_dialog('no.lists')

            else:
                lists = self.db.read_lists()
                data['lists_names'] = join_list(lists,
                                                self.translate('and'),
                                                lang=self.lang)
                data['list'] = self.plural_singular_form(lists)
                self.speak_dialog('read.lists', data)
Esempio n. 2
0
    def test_join_list_de(self):
        self.assertEqual(
            join_list(['Hallo', 'Auf wieder Sehen'], 'and', lang='de-de'),
            'Hallo und Auf wieder Sehen')

        self.assertEqual(join_list(['A', 'B', 'C'], 'or', lang='de-de'),
                         'A, B oder C')
Esempio n. 3
0
    def test_join(self):
        self.assertEqual(join_list(None, "and"), "")
        self.assertEqual(join_list([], "and"), "")

        self.assertEqual(join_list(["a"], "and"), "a")
        self.assertEqual(join_list(["a", "b"], "and"), "a and b")
        self.assertEqual(join_list(["a", "b"], "or"), "a or b")

        self.assertEqual(join_list(["a", "b", "c"], "and"), "a, b and c")
        self.assertEqual(join_list(["a", "b", "c"], "or"), "a, b or c")
        self.assertEqual(join_list(["a", "b", "c"], "or", ";"), "a; b or c")
        self.assertEqual(join_list(["a", "b", "c", "d"], "or"), "a, b, c or d")

        self.assertEqual(join_list([1, "b", 3, "d"], "or"), "1, b, 3 or d")
Esempio n. 4
0
    def ask_selection(self,
                      options,
                      dialog='',
                      data=None,
                      min_conf=0.65,
                      numeric=False):
        """Read options, ask dialog question and wait for an answer.

        This automatically deals with fuzzy matching and selection by number
        e.g.
            "first option"
            "last option"
            "second option"
            "option number four"

        Arguments:
              options (list): list of options to present user
              dialog (str): a dialog id or string to read AFTER all options
              data (dict): Data used to render the dialog
              min_conf (float): minimum confidence for fuzzy match, if not
                                reached return None
              numeric (bool): speak options as a numeric menu
        Returns:
              string: list element selected by user, or None
        """
        assert isinstance(options, list)

        if not len(options):
            return None
        elif len(options) == 1:
            return options[0]

        if numeric:
            for idx, opt in enumerate(options):
                opt_str = "{number}, {option_text}".format(
                    number=pronounce_number(idx + 1, self.lang),
                    option_text=opt)

                self.speak(opt_str, wait=True)
        else:
            opt_str = join_list(options, "or", lang=self.lang) + "?"
            self.speak(opt_str, wait=True)

        resp = self.get_response(dialog=dialog, data=data)

        if resp:
            match, score = match_one(resp, options)
            if score < min_conf:
                if self.voc_match(resp, 'last'):
                    resp = options[-1]
                else:
                    num = extract_number(resp, self.lang, ordinals=True)
                    resp = None
                    if num and num < len(options):
                        resp = options[num - 1]
            else:
                resp = match
        return resp
Esempio n. 5
0
    def handle_status(self, message):

        utt = message.data.get("utterance")

        if not len(self.settings["alarm"]):
            self.speak_dialog("alarms.list.empty")
            return

        status, alarms = self._get_alarm_matches(utt,
                                                 alarm=self.settings["alarm"],
                                                 max_results=3,
                                                 dialog='ask.which.alarm',
                                                 is_response=False)
        total = None
        desc = []
        if alarms:
            total = len(alarms)
            for alarm in alarms:
                desc.append(self._describe(alarm))

        items_string = ''
        if desc:
            items_string = join_list(desc, self.translate('and'))

        if status == 'No Match Found':
            self.speak_dialog('alarm.not.found')
        elif status == 'User Cancelled':
            return
        elif status == 'Next':
            reltime = nice_relative_time(
                self.get_alarm_local(alarms[0]), lang=self.lang
            )

            self.speak_dialog("next.alarm",
                              data={"when": self._describe(
                                                alarms[0]
                                            ),
                                    "duration": reltime})
        else:
            if total == 1:
                reltime = nice_relative_time(
                    self.get_alarm_local(alarms[0]), lang=self.lang
                )
                self.speak_dialog('alarms.list.single',
                                  data={'item': desc[0],
                                        'duration': reltime})
            else:
                self.speak_dialog('alarms.list.multi',
                                  data={'count': total,
                                        'items': items_string})
Esempio n. 6
0
 def _get_speakable_timer_list(self, timer_list):
     """ Get timer list as speakable string
     """
     speakable_timer_list = []
     for timer in timer_list:
         dialog = 'timer.details'
         if timer['name'] is not None:
             dialog += '.named'
         ordinal = (None if timer['ordinal'] <= 1
                    else self._get_speakable_ordinal(timer))
         if ordinal is not None:
             dialog += '.with.ordinal'
         data = {'ordinal': ordinal,
                 'duration': nice_duration(timer["duration"]),
                 'name': timer['name']}
         speakable_timer_list.append(self.translate(dialog, data))
     names = join_list(speakable_timer_list, self.translate("and"))
     return names
Esempio n. 7
0
    def _recur_desc(self, recur):
        # Create a textual description of the recur set
        day_list = list(recur)
        day_list.sort()
        days = " ".join(day_list)
        for r in self.recurrence_dict:
            if self.recurrence_dict[r] == days:
                return r  # accept the first perfect match

        # Assemble a long desc, e.g. "Monday and Wednesday"
        day_names = []
        for day in days.split(" "):
            for r in self.recurrence_dict:
                if self.recurrence_dict[r] == day:
                    day_names.append(r)
                    break

        return join_list(day_names, self.translate('and'))
Esempio n. 8
0
    def test_join_list_de(self):
        self.assertEqual(join_list(['Hallo', 'Auf wieder Sehen'], 'and'),
                         'Hallo und Auf wieder Sehen')

        self.assertEqual(join_list(['A', 'B', 'C'], 'or'),
                         'A, B oder C')
Esempio n. 9
0
    def _get_alarm_matches(self, utt, alarm=None, max_results=1,
                           dialog='ask.which.alarm', is_response=False):
        """Get list of alarms that match based on a user utterance.
        Arguments:
            utt (str): string spoken by the user
            alarm (list): list of alarm to match against
            max_results (int): max number of results desired
            dialog (str): name of dialog file used for disambiguation
            is_response (bool): is this being called by get_response
        Returns:
            (str): ["All", "Matched", "No Match Found", or "User Cancelled"]
            (list): list of matched alarm
        """
        alarms = alarm or self.settings['alarm']
        all_words = self.translate_list('all')
        next_words = self.translate_list('next')
        status = ["All", "Matched", "No Match Found", "User Cancelled", "Next"]

        # No alarms
        if alarms is None or len(alarms) == 0:
            self.log.error("Cannot get match. No active alarms.")
            return (status[2], None)

        # Extract Alarm Time
        when, utt_no_datetime = extract_datetime(utt, lang=self.lang) or (None, None)

        # Will return dt of unmatched string
        today = extract_datetime(self.texts.get('today'), lang=self.lang)
        today = today[0]

        # Check the time if it's midnight. This is to check if the user
        # said a recurring alarm with only the Day or if the user did
        # specify to set an alarm on midnight. If it's confirmed that
        # it's for a day only, then get another response from the user
        # to clarify what time on that day the recurring alarm is.
        is_midnight = self._check_if_utt_has_midnight(utt,
                                                      when,
                                                      self.threshold)

        if when == today and not is_midnight:
            when = None

        time_matches = None
        time_alarm = None
        if when:
            time_alarm = to_utc(when).timestamp()
            if is_midnight:
                time_alarm = time_alarm + 86400.0
            time_matches = [a for a in alarms
                            if abs(a["timestamp"] - time_alarm) <= 60]

        # Extract Recurrence
        recur = None
        recurrence_matches = None
        for word in self.recurrence_dict:
            is_match = self._fuzzy_match(word, utt.lower(), self.threshold)
            if is_match:
                recur = self._create_day_set(utt)
                alarm_recur = self._create_recurring_alarm(when, recur)
                recurrence_matches = [a for a in alarms
                    if a["repeat_rule"] == alarm_recur["repeat_rule"]
                ]
                break

        utt = utt_no_datetime or utt

        # Extract Ordinal/Cardinal Numbers
        number = extract_number(utt, ordinals=True, lang=self.lang)
        if number and number > 0:
            number = int(number)
        else:
            number = None

        # Extract Name
        name_matches = [a for a in alarms if a["name"] and
                        self._fuzzy_match(a["name"], utt, self.threshold)]

        # Match Everything
        alarm_to_match = None
        if when:
            if recur:
                alarm_to_match = alarm_recur
            else:
                alarm_to_match = {"timestamp": time_alarm, "repeat_rule": ""}

        # Find the Intersection of the Alarms list and all the matched alarms
        orig_count = len(alarms)
        if when and time_matches:
            alarms = [a for a in alarms if a in time_matches]
        if recur and recurrence_matches:
            alarms = [a for a in alarms if a in recurrence_matches]
        if name_matches:
            alarms = [a for a in alarms if a in name_matches]

        # Utterance refers to all alarms
        if utt and any(self._fuzzy_match(i, utt, 1) for i in all_words):
            return (status[0], alarms)
        # Utterance refers to the next alarm to go off
        elif utt and any(self._fuzzy_match(i, utt, 1) for i in next_words):
            return (status[4], [alarms[0]])

        # Given something to match but no match found
        if ((number and number > len(alarms)) or
                (recur and not recurrence_matches) or
                (when and not time_matches)):
            return (status[2], None)
        # If number of alarms filtered were the same, assume user asked for
        # All alarms
        if (len(alarms) == orig_count and max_results > 1 and
                not number and not when and not recur):
            return (status[0], alarms)
        # Return immediately if there is ordinal
        if number and number <= len(alarms):
            return (status[1], [alarms[number - 1]])
        # Return immediately if within maximum results
        elif alarms and len(alarms) <= max_results:
            return (status[1], alarms)
        # Ask for reply from user and iterate the function
        elif alarms and len(alarms) > max_results:
            desc = []
            for alarm in alarms:
                desc.append(self._describe(alarm))

            items_string = ''
            if desc:
                items_string = join_list(desc, self.translate('and'))

            reply = self.get_response(dialog, data={
                                                    'number': len(alarms),
                                                    'list': items_string,
                                                    }, num_retries=1)
            if reply:
                return self._get_alarm_matches(reply,
                                               alarm=alarms,
                                               max_results=max_results,
                                               dialog=dialog,
                                               is_response=True)
            else:
                return (status[3], None)

        # No matches found
        return (status[2], None)