def query_song(self, song: str, bonus=0.0): """ Try to find song :param self: :param song: :return: """ by_word = ' {} '.format(self.translate('by')) if len(song.split(by_word)) > 1: song, artist = song.split(by_word) self.log.info("Using search by artist") confidence, data = self.query_artist(artist) if confidence > 0.6: songs = self.client.search('artist', data['data']) #songtitles = [t['title'].lower() for t in songs] key, confidence = match_one(song, songs) return confidence + 0.1, {'data': {'title': key, 'artist': data['data']}, 'name': key, 'type': 'track'} else: return NOTHING_FOUND else: song_search = song if self.songs and len(self.songs) > 0: key, confidence = match_one(song, self.songs) return confidence + bonus, {'data': self.client.search('title', key)[0], 'name': key, 'type': 'track'} else: return NOTHING_FOUND
def _get_country_geometry(query, region=None, min_score=0.7): best_score = 0 best_match = None shapename = 'admin_0_countries' countries_shp = cartopy.io.shapereader.natural_earth( resolution='110m', category='cultural', name=shapename) for country in cartopy.io.shapereader.Reader(countries_shp).records(): country_name = country.attributes['NAME'].lower() country_long_name = country.attributes['NAME_LONG'].lower() reg = country.attributes["REGION_WB"].lower() subregion = country.attributes["SUBREGION"].lower() continent = country.attributes["CONTINENT"].lower() match, score = match_one(query.lower(), [country_long_name, country_name]) if region: _, score2 = match_one(region.lower(), [reg, subregion, continent]) score = (score + score2) / 2 if score > best_score: best_score = score best_match = country.geometry if best_score < min_score: best_match = None return best_match
def remove_by_name(self, name): """ removes the specified skill folder name """ LOG.info("searching skill by name: " + name) folders = self.skills.keys() names = [self.skills[skill]["name"] for skill in folders] f_skill, f_score = match_one(name, folders) n_skill, n_score = match_one(name, names) installed = False if n_score > 0.5: for s in self.skills: if self.skills[s]["name"] == n_skill: LOG.info("found skill by name") skill = self.skills[s] installed = skill["installed"] self.skills[s]["installed"] = False break elif f_score > 0.5: LOG.info("found skill by folder name") skill = self.skills[f_skill] installed = skill["installed"] self.skills[f_skill]["installed"] = False if not installed: LOG.warning("skill is not installed!") return False remove(skill["path"]) LOG.info("skill removed") return True
def handle_change_module_intent(self, message): self.validate_voices() module, voice = self._message_to_engine(message) # ask user if engine not explicit if not module: self.speak_dialog("engine.select", wait=True) module = self.ask_selection(self.available_engines) # fuzzy match transcription in case STT wasnt perfect if module is not None and module not in self.available_engines: words = message.data["utterance"].split(" ") for w in words: _voice, vscore = match_one(w, self.available_voices) if vscore >= 0.8: voice = _voice _engine, escore = match_one(w, self.available_engines) if escore >= 0.8: module = _engine # pick default voice if None selected if module is not None and voice is None and module in self.voices: self.log.debug("Default voice for engine " + module) voice = self.voices[module][0] # check if abort is needed if module is not None and module not in self.available_engines: self.speak_dialog("engine.invalid", {"engine": module}) self.speak_dialog("check.config") return if module is None: self.speak_dialog("noengine") return self.log.info("Selected TTS engine: " + str(module)) self.log.info("Selected TTS voice: " + str(voice)) self.change_voice(module, voice)
def _get_region_countries(self, query, min_score=0.7): countries = [] region, score = match_one(query, self.regions) if score > min_score - 0.15: countries = self.search_country_by_region(region) if score < min_score: region, score = match_one(query, self.subregions) if score > min_score: countries = self.search_country_by_subregion(region) return [c["name"] for c in countries]
def match_name_to_folder(self, name): LOG.info("searching skill by name: " + name) folders = self.skills.keys() names = [self.skills[skill]["name"] for skill in folders] f_skill, f_score = match_one(name, folders) n_skill, n_score = match_one(name, names) if n_score > 0.6: for s in self.skills: if self.skills[s]["name"] == n_skill: LOG.info("found skill by name") return s elif f_score > 0.6: LOG.info("found skill by folder name") return f_skill return None
def get_best_playlist(self, playlist): """ Get best playlist matching the provided name Arguments: playlist (str): Playlist name Returns: (str) best match, confidence, asin """ LOG.debug("playlist {}".format(playlist)) results = self.am.search(playlist, library_only=self.library_only, tracks=False, albums=False, playlists=True, artists=False, stations=False) playlists = {} for res in results: # LOG.debug(res[0]) if 'playlist' in res[0]: for hit in res[1]['hits']: title = hit['document']['title'].lower() asin = hit['document']['asin'] playlists[title] = { 'asin': asin, 'title': hit['document']['title'] } if playlists: key, confidence = match_one(playlist.lower(), list(playlists.keys())) LOG.debug("key {} confidence {}".format(key, confidence)) if confidence > 0.7: return key, confidence, playlists[key] return None, 0, None
def dialog_match(self, saved_dialog, skill): for path in self.paths_gen(skill): for root, dirs, files in os.walk(path): for f in files: ### for intent files filename = os.path.join(root, f) if self.lang in filename: ## reduce selection to reduce load list size if filename.endswith(".dialog"): try: match, confidence = match_one( self.saved_answer, self._lines_from_path(filename)) self.log.info("match " + str(match) + " confidence " + str(confidence)) if confidence > 0.8: saved_utt = self.get_response( "found.output", data={"match": match}) if saved_utt is not None: match, saved_utt = self.var_found( saved_utt, match) self.ask_save_intent_dialog( saved_utt, filename, match, skill) self.acknowledge() else: self.speak_dialog("cancel") except: self.log.info("fail load match: " + filename)
def query_album(self, album, bonus): """Try to find an album. Arguments: album (str): Album to search for bonus (float): Any bonus to apply to the confidence Returns: Tuple with confidence and data or NOTHING_FOUND """ data = None by_word = ' {} '.format(self.translate('by')) if len(album.split(by_word)) > 1: album, artist = album.split(by_word) album_search = album else: album_search = album albums = self.client.list('album') if len(albums) > 0: #albumlist = [a['album'].lower() for a in albums] key, confidence = match_one(album.lower(), self.albums) #album returns album name as data self.log.info("MPD Album: " + album + " matched to " + key + " with conf " + str(confidence)) #not the best tactic data = {'data': self.client.search('album', key)[0], 'name': key, 'type': 'album'} return confidence, data else: return NOTHING_FOUND
def work_on_intent(self, filename, skill, location): #self.log.info('find intent file: '+filename) if self.lang in filename: fobj = open(filename) self.log.info('open intent file: '+filename) for line in fobj: match, confidence = match_one(self.saved_utt, [line]) self.log.info('found intent: '+line.rstrip()+' '+str(confidence)) if confidence > 0.5: match, normal = self.normalise_question(match) self.log.info('match '+str(confidence)+' found and normal '+normal) if self.ask_yesno("do.you.mean", data={"match": normal}) is "yes": fobj.close() keywords = os.path.basename(filename).replace('.intent', '') self.log.info('keywords: '+keywords) question = self.saved_utt question_path = self.file_system.path+"/skills/"+str(skill)+"/"+"vocab"+"/"+self.lang self.log.info('bevor check: '+match) question = self.check_question(question, match) self.log.info('after check: '+match) self.log.info('output question: '+match) if self.ask_yesno("save.answer", data={"question": question, "skill": skill}) is "yes": self.save_intent(question_path, question, keywords) return break fobj.close()
def intent_match(self, saved_utt, skill): vocs = [] best_match = [0.5] for path in self.paths_gen(skill): self.log.info("search on path "+path) for root, dirs, files in os.walk(path): for f in files: ### for intent files filename = os.path.join(root, f) if filename.endswith(".intent"): i = filename.replace(".intent", "") #for l in self.read_intent_lines(i, filename): #self.log.info("test2"+str(self._lines_from_path(filename))) try: match, confidence = match_one(saved_utt, self._lines_from_path(filename)) self.log.info("match "+str(match)+ " confidence "+ str(confidence)) if confidence > best_match[0]: self.log.info("better match "+str(match)+ " confidence "+ str(confidence)) best_match = [confidence, saved_utt, filename, match, skill] self.log.info("save"+str(best_match)) except: self.log.info("fail load intent: "+filename) else: self.log.info(str(best_match[0])+" and") if len(best_match) > 1: match, saved_utt = self.var_found(best_match[1], best_match[3]) self.ask_save_intent_dialog(best_match[1], best_match[2], best_match[3], best_match[4]) self.acknowledge() match = self.filter_sentence(match) self.bus.emit(Message('recognizer_loop:utterance', {"utterances": [match], "lang": self.lang, "session": skill.name})) else: self.speak_dialog("no.old.inquiry")
def parse_substance(self, text): data = {} # correct drug name slang sub = self.caterpillar.fix_substance_names(text) if not sub: LOG.warn("probable bad substance name") else: text = sub # match drug to info best, score = match_one(text, self.substances) LOG.debug(str(score) + " " + best) if best in self.chemicals.keys(): data = self.chemicals[best] elif best in self.plants.keys(): data = self.plants[best] elif best in self.animals.keys(): data = self.animals[best] elif best in self.herbs.keys(): data = self.herbs[best] elif best in self.pharms.keys(): data = self.pharms[best] if data is None or score < 0.5: return False self.set_context("url", data["url"]) self.set_context("substance", data["name"]) LOG.info(str(data)) return data
def handle_apprise(self, message): if not self.tags: self.speak_dialog('setup.error') return all_keyword = self.translate("AllKeyword") tags = self.tags tags[all_keyword.lower()] = all_keyword self.log.debug("tags %s" % tags) tag = message.data.get("tag") text = message.data.get("text") self.log.debug("tag: %s - text: %s" % (tag, text)) success = False best_tag, score = match_one(tag.lower(), tags) self.log.debug("%s - %s" % (best_tag, score)) if score > 0.9: if best_tag == all_keyword: success = self.apobj.notify(text, title=text) else: success = self.apobj.notify(text, title=text, tag=best_tag) self.log.debug("result %s" % success) if success: self.speak_dialog('send.success', {'tag': best_tag}) else: self.speak_dialog('send.error', {'tag': best_tag})
def query_artist(self, artist, bonus=0.0): results = self.am.search(artist, library_only=self.library_only, tracks=False, albums=False, playlists=False, artists=True, stations=False) artists = {} for res in results: if 'artists' in res[0]: for hit in res[1]['hits']: name = hit['document']['name'].lower() asin = hit['document']['asin'] artists[name] = { 'asin': asin, 'name': hit['document']['name'] } if artists: key, confidence = match_one(artist.lower(), list(artists.keys())) if confidence > 0.7: confidence = min(confidence + bonus, 1.0) return (confidence, { 'asin': artists[key]['asin'], 'name': key, 'artist': artists[key]['name'], 'type': 'Artist' }) return None, None
def paths_gen(self, skill=None): if skill is None: skill = self.get_response("what.skill") if skill is None: self.speak_dialog("cancel") return else: if type(skill) is str: skill = self.get_skill(skill) paths = [] for folder in self.lang_paths: try: match, d = match_one(skill.name.replace("mycroft-", "").replace("skill", ""), os.listdir(folder)) paths.append(folder+"/"+match) except: self.log.info("skip folder"+folder) pass else: try: paths.append(skill.path) except: self.log.info("no skill path") pass self.log.info("test "+str(paths)) return paths
def skill_search(self, utter): """Redo with fuzzy mathcing""" skill, confidence = match_one(utter, self.examples_dict) if confidence > 0.5: return skill else: return None
def handle_tell_me_about(self, message): self.log.info(message.data.get('topic')) topic, conf = match_one(message.data.get('topic'), self.topics) point = [p for p in self.science_trail_points if p['name'] == topic] point = point[0] self.speak(point['snippet']) response = self.get_response(point['question'], num_retries=1) self.log.info('Submitting response for review')
def name_info(self, name): """ shows information about the skill matching <name> """ LOG.info("searching skill by name: " + name) folders = self.skills.keys() names = [self.skills[skill]["name"] for skill in folders] f_skill, f_score = match_one(name, folders) n_skill, n_score = match_one(name, names) if n_score > 0.5: for s in self.skills: if self.skills[s]["name"] == n_skill: LOG.info("skill found by name!") return self.skills[s] elif f_score > 0.5: LOG.info("skill found by folder name!") return self.skills[f_skill] LOG.warning("skill not found") return {}
def generic_query(self, phrase, bonus): """Check for a generic query, not asking for any special feature. This will try to parse the entire phrase in the following order - As a user playlist - As an album - As a track maybe cut out the direct responses Arguments: phrase (str): Text to match against bonus (float): Any existing match bonus Returns: Tuple with confidence and data or NOTHING_FOUND """ self.log.info('Handling "{}" as a generic query...'.format(phrase)) results = [] #check for playlist self.log.info('Checking playlists') conf, playlistdata = self.query_playlist(phrase) #decision if conf and conf > DIRECT_RESPONSE_CONFIDENCE: return conf, playlistdata elif conf and conf > MATCH_CONFIDENCE: results.append((conf, playlistdata)) #Check for Artist self.log.info('Checking artists') conf, data = self.query_artist(phrase) if conf and conf > DIRECT_RESPONSE_CONFIDENCE: return conf, data elif conf and conf > MATCH_CONFIDENCE: results.append((conf, data)) #Check for Track self.log.info('Checking tracks') if len(self.songs) > 0: key, conf = match_one(phrase.lower(), self.songs) #key = titles.index(key) self.log.info("Matched with " + key + " at " + str(conf)) track_data = self.client.search('title', key) data = {'data':track_data[0], 'name': key, 'type': 'track'} if conf and conf > DIRECT_RESPONSE_CONFIDENCE: return conf, data elif conf and conf > MATCH_CONFIDENCE: results.append((conf, data)) #Check for album self.log.info("Checking albums") conf, data = self.query_album(phrase, bonus) if conf and conf > DIRECT_RESPONSE_CONFIDENCE: return conf, data elif conf and conf > MATCH_CONFIDENCE: results.append((conf, data)) if len(results) == 0: return NOTHING_FOUND else: #return highest confidence value results.reverse() return sorted(results, key=lambda x: x[0])[-1]
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
def handle_news_intent(self, message): # clean a bit the utterance remainder = message.utterance_remainder() # fuzzy match available feeds feed, score = match_one(remainder, self.feeds.keys()) # if score is low fallback to default if score <= 0.5: feed = self.default_feed self.play_news(feed)
def handle_probe_distance(self, message): if self.probe_cache is None: self.probe_cache = probe_distances()["spaceprobe_distances"] probe = match_one(message.data["probe"].strip().replace(" ", "-"), list( self.probe_cache.keys())) distance = float(self.probe_cache[probe].lower()) self.speak_dialog("probe.distance", {"distance": distance, "probe": probe})
def handle_rain_waves_wind_intent(self, message): utterance = message.data.get('utterance', "") match, confidence = match_one(utterance, self.play_list) try: track_duration = int( extract_duration(utterance)[0].total_seconds()) except AttributeError: return None self.play_track(match, track_duration, utterance)
def score_likelihood(self, word_dict): resources = word_dict["resources"] # get from json scores = [] for resource in resources: # returns each intent scored by how similar they are to utterance # resource next to them to identify scores scores += [[resource, list(match_one(self.words, resources[resource]))]] return scores
def handle_pick_recipe(self, message): os.popen("skills/Energy-Monitor/./GUI.sh ") self.speak_dialog('pick.recipes') wait_while_speaking() title = message.data.get('title') score = match_one(title, self.play_list) print(score) if score[1] > 0.5: self.speak('your recipe is ') filepath = (score[0]) file1 = open("recipe.dat", "w") file1.write(filepath) file1.close() recipe = open(filepath, "r") recipe = recipe.read() TMP = os.popen( "$HOME/mycroft-core/skills/all-recipes-skill/bash/" + self.lang + "/./FILE " + filepath).read() IMAGE = ("/opt/mycroft/skills/all-recipes-skill/recipes/pics/" + TMP) IMAGE = IMAGE.strip() print(IMAGE) ########## FRACTION COINVERSION ############ Recipe_CONV = os.popen( "$HOME/mycroft-core/skills/all-recipes-skill/bash/" + self.lang + "/./FRAC " + filepath).read() file0 = open("recipe.conv", "w") file0.write(Recipe_CONV) file0.close() file1 = open("recipe2.dat", "w") file1.write('recipe.conv') file1.close() filepath = open("recipe2.dat", "r") path = filepath.read() ############################################# self.gui.show_image(IMAGE) time.sleep(3) self.is_reading = True with open(path) as fp: for cnt, line in enumerate(fp): if self.is_reading is False: break self.speak("{}".format(line), wait=True) self.gui['title'] = "FAVORITES" self.gui['reclist'] = " " self.gui['summary'] = recipe self.gui.show_page("recipe.qml", override_idle=600) else: #find return None self.speak('Sorry I could not find that recipe in my library')
def handle_pick_white_noise(self, message): self.speak_dialog('pick.white-noise') wait_while_speaking() title = message.data.get('title') score = match_one(title, self.play_list) print(score) if score[1] > 0.5: self.process = play_mp3(score[0]) else: return None self.speak('Sorry I could not find that white noise in my library')
def query_genre(self, genre: str, bonus = 0.0): """ :param genre: :param bonus: :return: """ key, confidence = match_one(genre, self.genre) if confidence > 0.7: return confidence, {'data': {'genre': key}, 'name':key, 'type': 'genre'} else: return NOTHING_FOUND
def setDirection(self, str): # build list of directions and destinations as strings dirList = [' '.join(x) for x in self.currentDirections] # select one destinaiton dirKey, dirConfidence = match_one(str, dirList) # set direction id to index of selected destination self.currentDirection = dirList.index(dirKey) return (self.currentDirections[self.currentDirection])
def get_best_playlist(self, playlist): """ Get best playlist matching the provided name Arguments: playlist (str): Playlist name Returns: (str) best match """ key, confidence = match_one(playlist, self.playlists.keys()) if confidence > 0.5: return key else: return None
def setStop(self, stopName): # build dictionay of stops if necessary self.getStops() # find closest match on route theStop = key, confidence = match_one(stopName, list(self.busStops)) self.stopId = self.busStops.get(key) # record stop name self.stopName = theStop[0] return (self.stopName)
def test_match_one(self): # test list of choices choices = ['frank', 'kate', 'harry', 'henry'] self.assertEqual(match_one('frank', choices)[0], 'frank') self.assertEqual(match_one('fran', choices)[0], 'frank') self.assertEqual(match_one('enry', choices)[0], 'henry') self.assertEqual(match_one('katt', choices)[0], 'kate') # test dictionary of choices choices = {'frank': 1, 'kate': 2, 'harry': 3, 'henry': 4} self.assertEqual(match_one('frank', choices)[0], 1) self.assertEqual(match_one('enry', choices)[0], 4)