def getattributes(uinput, context, attributes): '''This function marks the entities in user input, and updates the attributes dictionary''' # Can use context to to context specific attribute fetching if context.name.startswith('IntentComplete'): return attributes, uinput else: files = os.listdir('./entities/') entities = {} for fil in files: lines = open('./entities/' + fil).readlines() for i, line in enumerate(lines): lines[i] = line[:-1] entities[fil[:-4]] = '|'.join(lines) for entity in entities: for i in entities[entity].split('|'): if i.lower() in uinput.lower(): attributes[entity] = i for entity in entities: uinput = re.sub(entities[entity], r'$' + entity, uinput, flags=re.IGNORECASE) if context.name == 'num_passengers' and context.active: match = re.search('[0-9]+', uinput) if match: if int(match[0]) > 0: uinput = re.sub('[0-9]+', '$num_passengers', uinput) attributes['num_passengers'] = match.group() context.active = False else: num = text2int(uinput) if num > 0: uinput = '$num_passengers' attributes['num_passengers'] = num context.active = False elif context.name == 'luggage' and context.active: match = re.search('[0-9]+', uinput) if match: uinput = re.sub('[0-9]+', '$luggage', uinput) attributes['luggage'] = match.group() context.active = False else: num = text2int(uinput) uinput = '$luggage' attributes['luggage'] = num context.active = False return attributes, uinput
def parse(self, doc): sides, count = None, None for word in doc: if "d" in str(word).lower() or word.lemma_ == "coin": d_spl = str(word).lower().split("d") if word.lemma_ == "coin": sides = 2 else: if word.lemma_ in ["die", "dice"]: sides = 6 else: # get sides and count sides = int(d_spl[1]) if "d" in str(word).lower() and d_spl[0]: count = int(d_spl[0]) else: # get count from previous token count_token = word.nbor(-1) if str(count_token) == "a": count = 1 else: try: count = int(str(count_token)) except ValueError: count = utils.text2int(str(count_token)) return sides, count
def _to_num(text): if text == "to": return 2 elif text == "for": return 4 try: return int(text) except ValueError: return utils.text2int(text)
def run(self, doc): sentence = next(doc.sents) expression = "" if str(sentence.root) == "divide": for child in sentence.root.children: if child.dep_ == "dobj": try: value = int(str(child)) expression += str(value) except Exception as e: expression += str(utils.text2int(str(child))) expression += "/" elif child.dep_ == "prep" and str(child) == "by": try: value = int(str(next(child.children))) expression += str(value) except Exception as e: expression += str( utils.text2int(str(next(child.children)))) else: for token in sentence: print("{} - {}, {}".format(str(token), token.pos_, token.dep_)) if token.pos_ == "NUM": try: value = int(str(token)) expression += str(value) except Exception as e: expression += str(utils.text2int(str(token))) elif token.pos_ in ["CONJ", "VERB" ] or token.dep_ == "quantmod": if str(token) == "plus": expression += "+" elif str(token) == "minus": expression += "-" elif str(token) == "times": expression += "*" elif str(token.lemma_) == "divide" or str(token) == "over": expression += "/" elif token.pos_ == "SYM": expression += str(token) print("expression: {}".format(expression)) solution = eval(expression) # FIXME: potential security exploit here print("solution: {}".format(solution)) feedback.ShowNotify("{} = {}".format(expression, solution))
def parse_target_time( self, doc, now=datetime.datetime.now()) -> datetime.datetime: if utils.find_word(doc, ["hour", "minute", "second"]): all_time_ents = [ ent for ent in doc.ents if ent.label_ in ["TIME", "CARDINAL"] ] if len(all_time_ents) > 0: start_i = all_time_ents[0][0].i end_i = all_time_ents[-1][-1].i + 1 if end_i < len(doc): additional = utils.find_word(doc, ["hour", "minute", "second"], min_idx=end_i) while additional: # the entity parser didn't quite get all the tokens end_i = additional.i + 1 additional = utils.find_word( doc, ["hour", "minute", "second"], min_idx=end_i) else: time_word = utils.find_word(doc, ["hour", "minute", "second"]) start_i = utils.select_number_bleedy(time_word.nbor(-1))[0].i while time_word: end_i = time_word.i + 1 time_word = utils.find_word(doc, ["hour", "minute", "second"], min_idx=end_i) seconds = utils.parse_duration_to_seconds(doc[start_i:end_i]) return now + datetime.timedelta(seconds=seconds) else: all_time_ents = [ ent for ent in doc.ents if ent.label_ == "CARDINAL" or ent.label_ == "TIME" ] if len(all_time_ents) > 0: start_i = all_time_ents[0][0].i end_i = all_time_ents[-1][-1].i + 1 else: num_tokens = [ token for token in doc if token.like_num or token.text in ["am", "pm"] ] start_i = num_tokens[0].i end_i = num_tokens[-1].i + 1 if utils.find_word(doc, ["am", "pm"]): target_time, _ = cal.parseDT(doc[start_i:end_i].text, sourceTime=now) else: target_time = now.replace( hour=(utils.text2int(doc[start_i:end_i].text) + 12 * (now.hour < 12)) % 24, minute=0, second=0, microsecond=0) if target_time < now: target_time += datetime.timedelta(days=1) return target_time
def _to_string_num_filtered(s): s = str(s).replace(":", "").replace("-", "") if not s: return s if s == "to": return "2" if s == "for": return "4" if s in ["point", "dot"]: return "." try: float(s) return s except ValueError: return str(utils.text2int(str(s)))
def get_the_number(self, doc): answers = ["Nice, would you like to add something?", "Perfect, anything else?", "Well done, do you wish to add something else?"] num = 0 for j in doc: if j.text == 'a': num = 1 if j.pos_ == 'NUM' or (j.tag_ == 'LS' and j.pos_ == 'PUNCT'): try: num = int(j.lemma_) except ValueError: num = text2int(j.lemma_) break if num == 0: return "Please, specify a number!" else: self.orders[self.bar.get_drink(self.suggested_drink.name)] = num self.suggested_drink = None self.state = self.States.WAITING_ORDER return random.choice(answers)
def getTimeInSeconds(token): log.debug("orth: {}".format(token.orth_)) multiplier = 1 if "second" in token.orth_.lower(): multiplier = 1 elif "minute" in token.orth_.lower(): multiplier = 60 elif "hour" in token.orth_.lower(): multiplier = 60 * 60 elif "day" in token.orth_.lower(): multiplier = 60 * 60 * 24 elif str(token) in ["beginning", "start"]: multiplier = 0 #return int(str(token).split(" ")[0]) num = 0 for child in token.children: log.debug(f"{token} child: {child}") if child.like_num or child.text == "a": try: num = int(child.text) except ValueError: num = utils.text2int(child.text) return int(num) * multiplier
def parse(self, current_volume: float, sentence) -> float: """ current_voiume: The current volume in this context. sentence: spaCy parsed sentence returns the target volume as float, or a string, either "mute" or "unmute" """ assert isinstance(current_volume, float) assert not isinstance(sentence, str) percent = None volumeaction = None # TODO: rewrite this mess using the new util functions for word in sentence: if word.lemma_ in ["mute", "unmute"]: return word.lemma_ if word.lemma_ in ["increase", "decrease", "turn", "volume"]: for token in sentence: if token.i < word.i: continue if not volumeaction: if token.lemma_ == "up" or word.lemma_ == "increase": volumeaction = "increase" if token.lemma_ == "down" or word.lemma_ == "decrease": volumeaction = "decrease" if token.lemma_ == "to": volumeaction = "set" break if not volumeaction: volumeaction = "set" if str(sentence.root) == "set" or str( sentence.root) == "increase" or str( sentence.root) == "decrease": if volumeaction == None: volumeaction = str(sentence.root) elif sentence.root.text.lower() == "turn": for child in sentence.root.children: if child.dep_ == "prt": if str(child) == "up": if volumeaction == None: volumeaction = "increase" elif str(child) == "down": if volumeaction == None: volumeaction = "decrease" elif str(sentence.root) == "volume": for child in sentence.root.children: if str(child) in ["increase", "decrease"]: volumeaction = str(child) for child in sentence: #print ("child: {0}: {1}".format(child, child.dep_)) if child.dep_ == "prep": if str(child) == "to" or str(child) == "at": volumeaction = "set" elif str(child) == "by": pass else: continue for prepchild in child.children: #print ("prepchild: {0}: {1}".format(prepchild, prepchild.dep_)) if prepchild.dep_ == "pobj": #print(str(prepchild)) if str(prepchild) == "%" or str( prepchild) == "percent": for n in prepchild.children: #print ("n: {0}: {1}".format(n, n.dep_)) if n.dep_ == "nummod": p = str(n) break else: if prepchild.like_num and not prepchild.is_digit: numtext = utils.select_number_bleedy( prepchild).text percent = utils.text2int(numtext) / 100 else: p = str(prepchild) if not percent: percent = float(p.rstrip('%')) / 100 break elif child.is_digit: percent = float(child.text.rstrip('%')) / 100 if volumeaction and volumeaction != "set" and percent == None: percent = 0.10 log.debug( "percent unspecified, using arbitrary percentage: {}".format( percent)) if volumeaction == "increase": return round(current_volume + percent, 2) elif volumeaction == "decrease": return round(current_volume - percent, 2) if not percent: raise Exception("Unable to parse input") return round(percent, 2)
def parse(self, doc): """ Returns a i3-msg command with arguments to complete the action. """ workspace_token = utils.find_word(doc, ["workspace", "space", "desktop"]) workspace_number = None if workspace_token: num_token = workspace_token.nbor(1) if num_token.lemma_ == "number": num_token = workspace_token.nbor(2) # just in case the input filtering doesn't catch these cases if num_token.text.lower() in ["to", "for"]: workspace_number = { "to": 2, "for": 4 }[num_token.text.lower()] try: workspace_number = int(num_token.text) except: try: workspace_number = utils.text2int(num_token.text.lower()) except Exception as e: log.debug(f"Failed to parse workspace number: {e}") verb_word = utils.find_word(doc, ["switch", "focus", "show", "pull", "go", "move", "put", "kill", "close", "quit", "toggle", "enable", "disable", "make"]) # target_token indicates the target entity the request is referencing # used for requests like "show me steam" or "switch to the web browser" # FIXME: do something more robust target_token = utils.find_word(doc, ["this", "that", "steam", "browser", "firefox", "discord", "telegram", "calculator", "gedit", "editor", "studio", "blender", "spotify", "vlc"]) if target_token and target_token.text not in ["this", "that"]: matching_windows = self.find_matching_windows_in_tree(self.get_tree(), target_token.text.lower()) log.info(f"Found {len(matching_windows)} matching windows") command = None # switching workspaces if verb_word.lower_ in ["switch", "focus", "show", "pull", "go"]: if target_token: if len(matching_windows) > 0: command = f'i3-msg \'[con_id="{matching_windows[0]["id"]}"] focus\'' else: raise Exception("Could not find any windows matching query") elif workspace_token and workspace_number: command = f'i3-msg "workspace {workspace_number}"' else: # TODO: create Exception specifically for parsing failures raise Exception("Failed to parse input for workspace number") # moving windows to other workspaces elif verb_word.lower_ in ["move", "put"]: if workspace_token.nbor(-1).text in ["to", "on"] or (workspace_token.i >= 2 and workspace_token.nbor(-2).text in ["to", "on"]): # This means that we are moving a window to the target workspace if not workspace_token or not workspace_number: # TODO: create Exception specifically for parsing failures raise Exception("Unable to parse for target workspace") if target_token and target_token.text not in ["this", "that"]: if len(matching_windows) > 0: command = f'i3-msg \'[con_id="{matching_windows[0]["id"]}"] focus; move container to workspace number {workspace_number}\'' else: raise Exception("Could not find any windows matching query") elif target_token and target_token.text in ["this", "that"]: command = f'i3-msg "move container to workspace number {workspace_number}"' else: raise Exception("Failed to parse which program to move") else: # This means that we are moving the target workspace to a different output direction = utils.find_word(doc, ["up", "down", "left", "right", "primary"]) if not direction: raise Exception("Failed to parse which direction to move the current workspace") # if workspace_number: # NOTE: this is not yet supported by i3 # command = 'i3-msg "move workspace {} to output {}"'.format(workspace_number, direction.text) command = f'i3-msg "move workspace to output {direction.text}"' elif verb_word.lower_ in ["kill", "close", "quit"]: if target_token and target_token.text not in ["this", "that"]: if len(matching_windows) > 0: command = f'i3-msg \'[con_id="{matching_windows[0]["id"]}"] focus; kill\'' else: raise Exception("Could not find any windows matching query") elif target_token and target_token.text in ["this", "that"]: command = 'i3-msg "kill"' else: raise Exception("Failed to parse which program to kill") elif verb_word.lower_ in ["toggle", "enable", "disable", "make"]: verb_word = utils.find_word(doc, ["toggle", "enable", "disable", "make"]) attribute_word = utils.find_word(doc, ["fullscreen", "floating", "full", "float"]) if verb_word and attribute_word: verb = verb_word.text if verb == "make": verb = "enable" attribute = attribute_word.text if attribute == "full": attribute = "fullscreen" elif attribute == "float": attribute = "floating" if target_token and target_token.text not in ["this", "that"]: if len(matching_windows) > 0: command = f'i3-msg \'[con_id="{matching_windows[0]["id"]}"] focus; {attribute} {verb}\'' else: raise Exception("Could not find any windows matching query") else: command = f'i3-msg "{attribute} {verb}"' else: raise Exception(f"verb_word ({verb_word}) or attribute_word ({attribute_word}) not found") else: raise Exception(f"Unknown verb {verb_word.text}") return command
def delete_item(self, doc): # spacy returns verbs at infinity form with .lemma_ delete_verbs = ["remove", "delete", "drop"] ans_remove = ["I have removed [noun1].", "As you wish, so I've deleted [noun1].", "No problem [noun1] successfully removed!"] ans_ending = ["Do you wish to add or remove something?", "Do you wish to order or remove something else?", "Would you like to add or remove some other drinks?"] ans_donthave = ["You didn't order any [noun1].", "Sorry but you didn't take any [noun1]."] ans_not_understood = ["I am not programmed to understand the rest of what you just said.", "My circuits do not provide any info for the other items."] ans_recap = ["So far you have ordered [noun1].", "A quick recap of what you've ordered: [noun1],"] ans_invalid = ["I couldn't delete [noun1] because you went below zero.", "If I remove [noun1] you would end up with a negative order, so I cannot do it."] bad_items = {} # set() deleted_items = {} not_understood = False print(list(doc.noun_chunks)) # noun_chunks: spacy command which divides 'noun plus the words' describing the noun for span in doc.noun_chunks: root = span.root if root.dep_ == 'nsubj': # ex I or Mary , this noun_chunk is not relevant continue if (((root.pos_ == 'NOUN' or root.pos_ == "PROPN") and root.dep_ == 'dobj' and root.head.lemma_ in delete_verbs) or (root.dep_ == 'conj' and (root.head.pos_ == 'NOUN' or root.head.pos_ == "PROPN")) or (root.dep_ == 'appos' and (root.head.pos_ == 'NOUN' or root.head.pos_ == "PROPN"))): long_name = [] num = 1 for child in root.children: if child.dep_ == 'compound': long_name.append(child.text) if child.pos_ == 'NUM' and child.dep_ == 'nummod': try: num = int(child.lemma_) except ValueError: num = text2int(child.lemma_) long_name.append(root.lemma_) composed_name = '' for n, i in enumerate(long_name): if n == len(long_name) - 1: composed_name = composed_name + i else: composed_name = composed_name + i + ' ' composed_name = composed_name.lower() flag = True if composed_name in [drink.name for drink in self.orders.keys()]: deleted_items.setdefault(composed_name, 0) deleted_items[composed_name] += num # works also with unspecified number = 1 else: for category in Drink.CATEGORY: if composed_name in self.get_drink_list(category): flag = False bad_items[composed_name] = category # add(composed_name) # items not in the list if flag: not_understood = True answer = [] # Processing positive part: deleted_real = {} invalid_delete = {} if deleted_items: # self.state = self.States.WAITING_ORDER for item in deleted_items: # self.orders.setdefault(self.bar.get_drink(item), 0) old_n = self.orders[self.bar.get_drink(item)] if old_n > deleted_items[item]: self.orders[self.bar.get_drink(item)] -= deleted_items[item] deleted_real[item] = deleted_items[item] elif old_n == deleted_items[item]: del self.orders[self.bar.get_drink(item)] deleted_real[item] = deleted_items[item] else: invalid_delete[item] = deleted_items[item] if deleted_real: noun1 = join_with_and([str(num) + ' ' + item for item, num in deleted_real.items()]) part_neg = random.choice(ans_remove) part_neg = part_neg.replace('[noun1]', noun1) answer.append(part_neg) if invalid_delete: noun1 = join_with_and([str(num) + ' ' + item for item, num in invalid_delete.items()]) part_invalid = random.choice(ans_invalid) part_invalid = part_invalid.replace('[noun1]', noun1) answer.append(part_invalid) # Processing bad items part: if bad_items: part_donthave = random.choice(ans_donthave) noun1 = join_with_and(list(bad_items.keys())) part_donthave = part_donthave.replace('[noun1]', noun1) answer.append(part_donthave) # Recap part_recap = random.choice(ans_recap) noun1 = join_with_and([str(num) + ' ' + drink.name for drink, num in self.orders.items()]) part_recap = part_recap.replace("[noun1]", noun1) answer.append(part_recap) # Processing not understood part: if deleted_items and not bad_items and not_understood: answer.append(random.choice(ans_not_understood)) if len(answer) == 0: return None # Adding final question: answer.append(random.choice(ans_ending)) return ' '.join(answer)
def confirmation_suggestion(self, doc): positive_simple = ['yes', 'positive', 'okay', 'ok', 'alright', 'right', 'good', 'yeah', 'cool', 'course', 'yep', 'certainly', 'sure', 'fine'] positive_expression = ['of course', 'why not?', 'good idea', 'all right'] negative_simple = ['no', "nope", "enough", 'finished', 'nope', 'negative', 'modify'] answers_pos = ["Okay, I've just added it. Would you like to add something else?", "Perfect, anything else?", "You'll see, it's magnificent! Do you wish to add something else?"] answer_num = ["Excellent, how many [noun1] do you want?", "Perfect, how many [noun1] should I prepare?"] for token in doc: if token.text in positive_simple: num = 0 for j in doc: if j.text == 'a': num = 1 if j.pos_ == 'NUM': try: num = int(j.lemma_) except ValueError: num = text2int(j.lemma_) break if num != 0: self.orders[self.bar.get_drink(self.suggested_drink.name)] = num self.suggested_drink = None self.state = self.States.WAITING_ORDER return random.choice(answers_pos) else: self.state = self.States.NUMBER_SUGGESTED # print(self.suggested_drink.name) answer = random.choice(answer_num) answer = answer.replace('[noun1]', self.suggested_drink.name) return answer if token.text in negative_simple: self.suggested_drink = None self.state = self.States.WAITING_ORDER return "No problem, so what else would you like?" for phrase in positive_expression: if phrase in doc.text: num = 0 for j in doc: if j.text == 'a': num = 1 if j.pos_ == 'NUM': try: num = int(j.lemma_) except ValueError: num = text2int(j.lemma_) break if num != 0: self.orders[self.bar.get_drink(self.suggested_drink.name)] = num self.suggested_drink = None self.state = self.States.WAITING_ORDER return random.choice(answers_pos) else: self.state = self.States.NUMBER_SUGGESTED # print(self.suggested_drink.name) answer = random.choice(answer_num) answer = answer.replace('[noun1]', self.suggested_drink.name) return answer
def specific_order(self, doc): # spacy returns verbs at infinity form with .lemma_ ordering_verbs = ["order", "like", "have", "take", "make", "give", "want", "get", "buy", "add"] ans_pos = ["Ok I will add [noun1] to the list!", "Ok I have added [noun1] to the order.", "Good choice with [noun1]."] ans_donthave = ["Unfortunately we don't have [noun1].", "Unfortunately we ran out of [noun1].", "Sorry but we don't have any [noun1]."] ans_not_understood = ["I am not programmed to understand the rest of what you just said.", "My circuits do not provide any info for the other items."] ans_suggest = ["I can suggest you a fresh [noun1]. Would you like it?"] ans_ending = ["Would you like something else?", "Do you wish to order something else?", "Would you like to add some other drinks?"] bad_items = {} # set() ordered_items = {} not_understood = False print(list(doc.noun_chunks)) # noun_chunks: spacy command which divides 'noun plus the words' describing the noun for span in doc.noun_chunks: root = span.root if root.dep_ == 'nsubj': # ex I or Mary , this noun_chunk is not relevant continue # nome puntato dal verbo di odinazione o nome puntato da un altro nome (puntato da verbo di ordinazione) if (((root.pos_ == 'NOUN' or root.pos_ == "PROPN") and root.dep_ == 'dobj' and root.head.lemma_ in ordering_verbs) or (root.dep_ == 'conj' and (root.head.pos_ == 'NOUN' or root.head.pos_ == "PROPN")) or (root.dep_ == 'appos' and (root.head.pos_ == 'NOUN' or root.head.pos_ == "PROPN"))): long_name = [] num = 1 for child in root.children: if child.dep_ == 'compound': long_name.append(child.text) if child.pos_ == 'NUM' and child.dep_ == 'nummod': try: num = int(child.lemma_) except ValueError: num = text2int(child.lemma_) long_name.append(root.lemma_) composed_name = '' for n, i in enumerate(long_name): if n == len(long_name) - 1: composed_name = composed_name + i else: composed_name = composed_name + i + ' ' composed_name = composed_name.lower() flag = True if composed_name in [drink.name for drink in self.bar.get_drinks()]: ordered_items.setdefault(composed_name, 0) ordered_items[composed_name] += num # works also with unspecified number = 1 else: for category in Drink.CATEGORY: if composed_name in self.get_drink_list(category): flag = False bad_items[composed_name] = category # add(composed_name) # items not in the list if flag: not_understood = True answer = [] # Processing positive part: if ordered_items: self.state = self.States.WAITING_ORDER for item in ordered_items: self.orders.setdefault(self.bar.get_drink(item), 0) self.orders[self.bar.get_drink(item)] += ordered_items[item] if not not_understood and not bad_items: noun1 = 'that' else: noun1 = join_with_and([str(num) + ' ' + item for item, num in ordered_items.items()]) part_pos = random.choice(ans_pos) part_pos = part_pos.replace('[noun1]', noun1) answer.append(part_pos) # Processing bad items part: if bad_items: part_donthave = random.choice(ans_donthave) noun1 = join_with_and(list(bad_items.keys())) part_donthave = part_donthave.replace('[noun1]', noun1) answer.append(part_donthave) # Giving a suggestion: if len(bad_items) > 1 and not ordered_items: ordered_categories = list(bad_items.values()) part_suggest = '' for cat in ordered_categories: intro = 'Our list of ' + cat + 's is the following: ' part_suggest = part_suggest + intro + join_with_and([drink.name for drink in self.bar.get_drinks(cat)]) answer.append(part_suggest) elif len(bad_items) == 1: self.state = self.States.ACCEPT_SUGGESTION bad_item = list(bad_items.keys())[0] a = self.suggest(ordered_items, category=bad_items[bad_item]) self.suggested_drink = a part_suggest = random.choice(ans_suggest) part_suggest = part_suggest.replace('[noun1]', a.name) answer.append(part_suggest) # Processing not understood part: if ordered_items and not bad_items and not_understood: answer.append(random.choice(ans_not_understood)) if len(answer) == 0: return None # Adding final question: if not len(bad_items) == 1: answer.append(random.choice(ans_ending)) return ' '.join(answer)
def extract_parameters(self, doc): """ Extracts action and parameters for human_input Returns a tuple, a string of the action and a tuple of the parameters. """ sentence = next(doc.sents) inputaction = None # extract the action for word in sentence: if word.lemma_ in ["click", "move", "press", "scroll"]: inputaction = word.lemma_ break if word.lemma_ in ["type", "dictate"]: inputaction = "type" break # default parameters click_param = "left" scroll_direction = "down" scroll_amount = 0 move_direction = "" move_amount = 0 press_param = "" type_param = "" # extract the parameters if inputaction == "click": word = utils.find_word(sentence.doc, ["left","middle","right","double","triple"]) if word: click_param = word.lemma_ elif inputaction == "scroll": for word in sentence: if str(word) in ["up", "down"]: scroll_direction = word.lemma_ scroll_amount = 8 elif str(word) in ["top", "bottom"]: scroll_direction = {"top":"up", "bottom":"down"}[str(word)] scroll_amount = 1000 elif inputaction == "move": unit_size = 10 for word in sentence: numToken = None if str(word) in ["up", "down", "left", "right", "center"]: move_direction = str(word) elif word.dep_ == "prep": if str(word) == "by": for prepchild in word.children: if prepchild.dep_ == "pobj": if prepchild.lemma_ in ["pixel", "unit"]: if prepchild.lemma_ == "pixel": unit_size = 1 for c in prepchild.children: if c.like_num: numToken = c elif prepchild.like_num: numToken = prepchild elif word.lemma_ in ["pixel", "unit"]: if word.lemma_ == "pixel": unit_size = 1 for c in word.children: if c.like_num: numToken = c elif word.like_num: numToken = word if numToken: try: move_amount = int(str(numToken)) except: try: move_amount = utils.text2int(str(numToken)) except Exception as e: log.error("could not parse {}".format(numToken)) break move_amount *= unit_size elif inputaction == "press": word = utils.find_word(sentence.doc, ["press"]) if word and word.nbor(1): press_param = '+'.join(map(str, sentence.doc[word.i + 1:])) elif inputaction == "type": word = utils.find_word(sentence.doc, ["type", "dictate"]) if word and word.nbor(1): objective_span = sentence.doc[word.i + 1:] if len(objective_span) >= 3 and str(objective_span[0:2]).startswith("the word"): symbol_to_word = { "(": "parenthesis", ")": "closed parenthesis", "[": "square bracket", "]": "closed square bracket", "&": "ampersand", } type_param = symbol_to_word[str(objective_span[2])] else: type_param = ' '.join(map(str, objective_span)) # remove spaces in front of dollar signs if preceding a number dollar_with_num_regex = re.compile(r"(\$) (\d)") type_param = re.sub(dollar_with_num_regex, lambda m: m.group(1) + m.group(2), type_param) # HACK: quick fix to make dictating longer numbers easier broken_num_regex = re.compile(r"((\d+\.?(\d+)?|\d*\.(\d+)).([A-Za-z-:]+| |\d)|for|to)\s?\d+") if broken_num_regex.match(type_param): def _to_string_num_filtered(s): s = str(s).replace(":", "").replace("-", "") if not s: return s if s == "to": return "2" if s == "for": return "4" if s in ["point", "dot"]: return "." try: float(s) return s except ValueError: return str(utils.text2int(str(s))) type_param = ''.join(map(_to_string_num_filtered, objective_span)) if inputaction == "click": return inputaction, (click_param,) elif inputaction == "scroll": return inputaction, (scroll_direction, scroll_amount,) elif inputaction == "move": return inputaction, (move_direction, move_amount) elif inputaction == "press": return inputaction, (press_param,) elif inputaction == "type": return inputaction, (type_param,)